From 8e4868e315a2d82cf82abc2190d39b088e53eafe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 29 Jan 2024 11:27:32 +1030 Subject: [PATCH 01/38] lnprototest: remove. At this point it needs a complete rewrite to be useful, and it's just constraining development. Signed-off-by: Rusty Russell --- .github/workflows/prototest.yaml | 37 -------------------------------- .gitmodules | 3 --- Makefile | 11 ++-------- external/Makefile | 3 +-- external/lnprototest | 1 - 5 files changed, 3 insertions(+), 52 deletions(-) delete mode 100644 .github/workflows/prototest.yaml delete mode 160000 external/lnprototest diff --git a/.github/workflows/prototest.yaml b/.github/workflows/prototest.yaml deleted file mode 100644 index f333ef880551..000000000000 --- a/.github/workflows/prototest.yaml +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: LN Proto Test -on: - push: - branches: - - "master" - pull_request: -jobs: - proto-test: - name: Protocol Test Config - runs-on: ubuntu-22.04 - timeout-minutes: 120 - strategy: - fail-fast: true - matrix: - include: - - {compiler: clang, db: sqlite3} - - {compiler: gcc, db: postgres} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Build and run - run: | - 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 PYTEST_PAR=2 \ - -e PYTEST_OPTS="--timeout=300" \ - -e TEST_CMD="make check-protos" \ - -e TEST_GROUP=1 \ - -e TEST_GROUP_COUNT=1 \ - -e TIMEOUT=120 \ - cln-ci-ubuntu diff --git a/.gitmodules b/.gitmodules index 6dae35a54444..4788bedb3fd4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,9 +14,6 @@ [submodule "external/gheap"] path = external/gheap url = https://github.com/valyala/gheap -[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/Makefile b/Makefile index 4ebd478924e5..e6da7a1b8d10 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ endif # (method=thread to support xdist) 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 +MY_CHECK_PYTHONPATH=$${PYTHONPATH}$${PYTHONPATH:+:}$(shell pwd)/contrib/pyln-client:$(shell pwd)/contrib/pyln-testing:$(shell pwd)/contrib/pyln-proto/:$(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= \ contrib/pyln-grpc-proto/pyln/grpc/primitives_pb2.py \ @@ -453,14 +453,7 @@ endif check-units: -check: check-units installcheck check-protos pytest - -check-protos: $(ALL_PROGRAMS) -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 - @(cd external/lnprototest && PYTHONPATH=$(MY_CHECK_PYTHONPATH) LIGHTNING_SRC=../.. $(PYTEST) --runner lnprototest.clightning.Runner $(PYTEST_OPTS)) -endif +check: check-units installcheck pytest pytest: $(ALL_PROGRAMS) $(DEFAULT_TARGETS) $(ALL_TEST_PROGRAMS) $(ALL_TEST_GEN) ifeq ($(PYTEST),) diff --git a/external/Makefile b/external/Makefile index 2767cc06885d..e199108bf0c1 100644 --- a/external/Makefile +++ b/external/Makefile @@ -3,8 +3,7 @@ SUBMODULES = \ external/libwally-core \ external/gheap \ external/jsmn \ - external/libbacktrace \ - external/lnprototest + external/libbacktrace ifeq ($(HAVE_LOWDOWN),0) SUBMODULES += external/lowdown diff --git a/external/lnprototest b/external/lnprototest deleted file mode 160000 index a8d4dcf6b859..000000000000 --- a/external/lnprototest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a8d4dcf6b859f3c11c92a917f7c48b2051dbb4c4 From fcba09ab0fa00b0037d63e1d09e88c58a91931e0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:16 +1030 Subject: [PATCH 02/38] lightningd: use explicit accessor to get channel_update for errors. We want this later, so do the prep work now. Signed-off-by: Rusty Russell --- lightningd/channel.h | 5 ++++- lightningd/gossip_control.c | 11 ++++++++--- lightningd/peer_htlcs.c | 16 +++++++++------- wallet/test/run-wallet.c | 7 ++++--- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/lightningd/channel.h b/lightningd/channel.h index bf70c249ae9d..c2893fc09d89 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -788,7 +788,10 @@ 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); +const u8 *channel_update_for_error(const tal_t *ctx, + struct channel *channel); struct amount_msat htlc_max_possible_send(const struct channel *channel); + + #endif /* LIGHTNING_LIGHTNINGD_CHANNEL_H */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index b4083c29ad6b..6e2df87ffb20 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -162,15 +162,20 @@ static void handle_local_channel_update(struct lightningd *ld, const u8 *msg) channel_replace_update(channel, take(update)); } -const u8 *get_channel_update(struct channel *channel) +const u8 *channel_update_for_error(const tal_t *ctx, + struct channel *channel) { + if (!channel->channel_update) + return NULL; + /* Tell gossipd we're using it (if shutting down, might be NULL) */ - if (channel->channel_update && channel->peer->ld->gossip) { + if (channel->peer->ld->gossip && channel->scid) { subd_send_msg(channel->peer->ld->gossip, take(towire_gossipd_used_local_channel_update (NULL, channel->scid))); } - return channel->channel_update; + + return tal_dup_talarr(ctx, u8, channel->channel_update); } static void set_channel_remote_update(struct lightningd *ld, diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 36b81760915c..2cd4e289f428 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -491,7 +491,8 @@ static void destroy_hout_subd_died(struct htlc_out *hout) hout->key.id); hout->failmsg = towire_temporary_channel_failure(hout, - get_channel_update(hout->key.channel)); + channel_update_for_error(tmpctx, + hout->key.channel)); /* Assign a temporary state (we're about to free it!) so checks * are happy that it has a failure message */ @@ -529,7 +530,7 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds UNU if (tal_count(failmsg)) { /* It's our job to append the channel_update */ if (fromwire_peektype(failmsg) & UPDATE) { - const u8 *update = get_channel_update(hout->key.channel); + const u8 *update = channel_update_for_error(tmpctx, hout->key.channel); towire(&failmsg, update, tal_bytelen(update)); } hout->failmsg = tal_steal(hout, failmsg); @@ -626,7 +627,7 @@ const u8 *send_htlc_out(const tal_t *ctx, log_info(out->log, "Attempt to send HTLC but unowned (%s)", channel_state_name(out)); return towire_temporary_channel_failure(ctx, - get_channel_update(out)); + channel_update_for_error(tmpctx, out)); } if (!topology_synced(out->peer->ld->topology)) { @@ -749,7 +750,8 @@ static void forward_htlc(struct htlc_in *hin, next->old_feerate_base, next->old_feerate_ppm)) { failmsg = towire_fee_insufficient(tmpctx, hin->msat, - get_channel_update(next)); + channel_update_for_error(tmpctx, + next)); goto fail; } log_info(hin->key.channel->log, @@ -763,7 +765,7 @@ static void forward_htlc(struct htlc_in *hin, || 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)); + channel_update_for_error(tmpctx, next)); goto fail; } log_info(hin->key.channel->log, @@ -773,7 +775,7 @@ static void forward_htlc(struct htlc_in *hin, if (!check_cltv(hin, cltv_expiry, outgoing_cltv_value, ld->config.cltv_expiry_delta)) { failmsg = towire_incorrect_cltv_expiry(tmpctx, cltv_expiry, - get_channel_update(next)); + channel_update_for_error(tmpctx, next)); goto fail; } @@ -792,7 +794,7 @@ static void forward_htlc(struct htlc_in *hin, outgoing_cltv_value, get_block_height(ld->topology)); failmsg = towire_expiry_too_soon(tmpctx, - get_channel_update(next)); + channel_update_for_error(tmpctx, next)); goto fail; } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 1ef36ffb6705..b7a87c10d3ac 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -88,6 +88,10 @@ void channel_unsaved_close_conn(struct channel *channel UNNEEDED, const char *wh /* Generated stub for channel_update_feerates */ void channel_update_feerates(struct lightningd *ld UNNEEDED, const struct channel *channel UNNEEDED) { fprintf(stderr, "channel_update_feerates called!\n"); abort(); } +/* Generated stub for channel_update_for_error */ +const u8 *channel_update_for_error(const tal_t *ctx UNNEEDED, + struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_update_for_error called!\n"); abort(); } /* Generated stub for channel_update_reserve */ void channel_update_reserve(struct channel *channel UNNEEDED, struct channel_config *their_config UNNEEDED, @@ -296,9 +300,6 @@ bool fromwire_openingd_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 hsmd_wire_name */ const char *hsmd_wire_name(int e UNNEEDED) { fprintf(stderr, "hsmd_wire_name called!\n"); abort(); } From 4dca6179dbcc63fb40f43d2a63976b409dea8c89 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:16 +1030 Subject: [PATCH 03/38] hsmd: add command to allow lightningd to sign channel announcement. Before this it was channeld doing it, which was tied to a particular channel. Create an API for lightningd to sign for any channel. Signed-off-by: Rusty Russell --- common/hsm_version.h | 3 +- hsmd/hsmd.c | 2 ++ hsmd/hsmd_wire.csv | 10 ++++++ hsmd/libhsmd.c | 72 +++++++++++++++++++++++++++++++++----------- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/common/hsm_version.h b/common/hsm_version.h index 5ca445ce2962..5a2c2deac84a 100644 --- a/common/hsm_version.h +++ b/common/hsm_version.h @@ -21,7 +21,8 @@ * v4 with buried outpoint check: f44fae666895cab0347b3de7c245267c71cc7de834827b83e286e86318c08aec * v4 with forget_channel: d87c6934ea188f92785d38d7cd0b13ed7f76aa7417f3200baf0c7b5aa832fe29 * v5 with hsmd_revoke_commitment_tx: 5742538f87ef5d5bf55b66dc19e52c8683cfeb1b887d3e64ba530ba9a4d8e638 - */ + * v5 with sign_any_cannouncement: 5fdb9068c43a21887dc03f7dce410d2e3eeff6277f0d49b4fc56595a798fd4a4 +*/ #define HSM_MIN_VERSION 3 #define HSM_MAX_VERSION 5 #endif /* LIGHTNING_COMMON_HSM_VERSION_H */ diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index d557799aed52..1aec53028500 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -671,6 +671,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: case WIRE_HSMD_DERIVE_SECRET: case WIRE_HSMD_CANNOUNCEMENT_SIG_REQ: + case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REQ: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REQ: case WIRE_HSMD_CUPDATE_SIG_REQ: case WIRE_HSMD_SIGN_LOCAL_HTLC_TX: @@ -722,6 +723,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_CHECK_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: + case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REPLY: return bad_req_fmt(conn, c, c->msg_in, "Received an incoming message of type %s, " "which is not a request", diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 3b0c9c230e30..98e3bd021596 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -176,6 +176,16 @@ msgtype,hsmd_cannouncement_sig_reply,102 msgdata,hsmd_cannouncement_sig_reply,node_signature,secp256k1_ecdsa_signature, msgdata,hsmd_cannouncement_sig_reply,bitcoin_signature,secp256k1_ecdsa_signature, +msgtype,hsmd_sign_any_cannouncement_req,4 +msgdata,hsmd_sign_any_cannouncement_req,calen,u16, +msgdata,hsmd_sign_any_cannouncement_req,ca,u8,calen +msgdata,hsmd_sign_any_cannouncement_req,peerid,node_id, +msgdata,hsmd_sign_any_cannouncement_req,channel_dbid,u64, + +msgtype,hsmd_sign_any_cannouncement_reply,104 +msgdata,hsmd_sign_any_cannouncement_reply,node_signature,secp256k1_ecdsa_signature, +msgdata,hsmd_sign_any_cannouncement_reply,bitcoin_signature,secp256k1_ecdsa_signature, + msgtype,hsmd_cupdate_sig_req,3 msgdata,hsmd_cupdate_sig_req,culen,u16, msgdata,hsmd_cupdate_sig_req,cu,u8,culen diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index a7686ea24ed8..a6cacdb90e38 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -139,6 +139,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_ANY_LOCAL_HTLC_TX: case WIRE_HSMD_SIGN_ANCHORSPEND: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE: + case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REQ: return (client->capabilities & HSM_PERM_MASTER) != 0; /*~ These are messages sent by the HSM so we should never receive them. */ @@ -178,6 +179,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_CHECK_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: + case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REPLY: break; } return false; @@ -972,7 +974,12 @@ static u8 *handle_get_output_scriptpubkey(struct hsmd_client *c, * defined in BOLT #7, and requires *two* signatures: one from this node's key * (to prove it's from us), and one from the bitcoin key used to create the * funding transaction (to prove we own the output). */ -static u8 *handle_cannouncement_sig(struct hsmd_client *c, const u8 *msg_in) +static const char *handle_sign_cannouncement(const tal_t *ctx, + const struct node_id *peer_id, + u64 dbid, + const u8 *ca, + secp256k1_ecdsa_signature *node_sig, + secp256k1_ecdsa_signature *bitcoin_sig) { /*~ Our autogeneration code doesn't define field offsets, so we just * copy this from the spec itself. @@ -992,10 +999,7 @@ static u8 *handle_cannouncement_sig(struct hsmd_client *c, const u8 *msg_in) /* First type bytes are the msg type */ size_t offset = 2 + 256; struct privkey node_pkey; - secp256k1_ecdsa_signature node_sig, bitcoin_sig; struct sha256_double hash; - u8 *reply; - u8 *ca; struct pubkey funding_pubkey; struct privkey funding_privkey; struct secret channel_seed; @@ -1009,32 +1013,61 @@ static u8 *handle_cannouncement_sig(struct hsmd_client *c, const u8 *msg_in) * mind if you fix this for him! */ /* FIXME: We should cache these. */ - get_channel_seed(&c->id, c->dbid, &channel_seed); + get_channel_seed(peer_id, dbid, &channel_seed); derive_funding_key(&channel_seed, &funding_pubkey, &funding_privkey); + if (tal_count(ca) < offset) + return tal_fmt(ctx, "bad cannounce length %zu", tal_count(ca)); + + if (fromwire_peektype(ca) != WIRE_CHANNEL_ANNOUNCEMENT) + return tal_fmt(ctx, "Invalid channel announcement"); + + node_key(&node_pkey, NULL); + sha256_double(&hash, ca + offset, tal_count(ca) - offset); + + sign_hash(&node_pkey, &hash, node_sig); + sign_hash(&funding_privkey, &hash, bitcoin_sig); + return NULL; +} + +static u8 *handle_cannouncement_sig(struct hsmd_client *c, const u8 *msg_in) +{ + u8 *ca; + secp256k1_ecdsa_signature node_sig, bitcoin_sig; + const char *err; + /*~ fromwire_ routines which need to do allocation take a tal context * as their first field; tmpctx is good here since we won't need it * after this function. */ if (!fromwire_hsmd_cannouncement_sig_req(tmpctx, msg_in, &ca)) return hsmd_status_malformed_request(c, msg_in); - if (tal_count(ca) < offset) - return hsmd_status_bad_request_fmt( - c, msg_in, "bad cannounce length %zu", tal_count(ca)); + err = handle_sign_cannouncement(tmpctx, &c->id, c->dbid, ca, + &node_sig, &bitcoin_sig); + if (err) + return hsmd_status_bad_request_fmt(c, msg_in, "%s", err); - if (fromwire_peektype(ca) != WIRE_CHANNEL_ANNOUNCEMENT) - return hsmd_status_bad_request_fmt( - c, msg_in, "Invalid channel announcement"); + return towire_hsmd_cannouncement_sig_reply(NULL, &node_sig, &bitcoin_sig); +} - node_key(&node_pkey, NULL); - sha256_double(&hash, ca + offset, tal_count(ca) - offset); +/* This variant is used by modern lightningd to sign for a particular channel */ +static u8 *handle_any_cannouncement_sig(struct hsmd_client *c, const u8 *msg_in) +{ + u8 *ca; + struct node_id peer_id; + u64 dbid; + secp256k1_ecdsa_signature node_sig, bitcoin_sig; + const char *err; + + if (!fromwire_hsmd_sign_any_cannouncement_req(tmpctx, msg_in, &ca, &peer_id, &dbid)) + return hsmd_status_malformed_request(c, msg_in); - sign_hash(&node_pkey, &hash, &node_sig); - sign_hash(&funding_privkey, &hash, &bitcoin_sig); + err = handle_sign_cannouncement(tmpctx, &peer_id, dbid, ca, + &node_sig, &bitcoin_sig); + if (err) + return hsmd_status_bad_request_fmt(c, msg_in, "%s", err); - reply = towire_hsmd_cannouncement_sig_reply(NULL, &node_sig, - &bitcoin_sig); - return reply; + return towire_hsmd_sign_any_cannouncement_reply(NULL, &node_sig, &bitcoin_sig); } /*~ It's optional for nodes to send node_announcement, but it lets us set our @@ -2026,6 +2059,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_get_channel_basepoints(client, msg); case WIRE_HSMD_CANNOUNCEMENT_SIG_REQ: return handle_cannouncement_sig(client, msg); + case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REQ: + return handle_any_cannouncement_sig(client, msg); case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REQ: return handle_sign_node_announcement(client, msg); case WIRE_HSMD_CUPDATE_SIG_REQ: @@ -2110,6 +2145,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_CHECK_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: + case WIRE_HSMD_SIGN_ANY_CANNOUNCEMENT_REPLY: break; } return hsmd_status_bad_request(client, msg, "Unknown request"); From 9b6e7689615119cc77250ef4bad91aed2e454f71 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:16 +1030 Subject: [PATCH 04/38] gossipd: strip private updates from gossip_store on startup. We rename them to _obs, too. Signed-off-by: Rusty Russell --- common/gossip_store.h | 2 +- devtools/dump-gossipstore.c | 4 +- gossipd/gossip_generation.c | 2 +- gossipd/gossip_store.c | 107 +++++++++++++++++++++++----------- gossipd/gossip_store_wire.csv | 14 ++--- gossipd/routing.c | 32 +++++----- gossipd/routing.h | 10 ++++ plugins/sql.c | 4 +- 8 files changed, 111 insertions(+), 64 deletions(-) diff --git a/common/gossip_store.h b/common/gossip_store.h index ef6394dd7ce6..f1a8525df79a 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 == 11. + * As of this writing, major == 0, minor == 13. */ #define GOSSIP_STORE_MAJOR_VERSION_MASK 0xE0 #define GOSSIP_STORE_MINOR_VERSION_MASK 0x1F diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 3df77a92682e..09c6f3a1e428 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -113,12 +113,12 @@ int main(int argc, char *argv[]) printf("t=%u node_announcement: %s\n", be32_to_cpu(hdr.timestamp), tal_hex(msg, msg)); - } else if (fromwire_gossip_store_private_channel(msg, msg, &sat, + } else if (fromwire_gossip_store_private_channel_obs(msg, msg, &sat, &inner)) { printf("private channel_announcement: %s %s\n", type_to_string(tmpctx, struct amount_sat, &sat), tal_hex(msg, inner)); - } else if (fromwire_gossip_store_private_update(msg, msg, + } else if (fromwire_gossip_store_private_update_obs(msg, msg, &inner)) { printf("private channel_update: %s\n", tal_hex(msg, inner)); diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 84532bc19218..29787231e1a3 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -720,7 +720,7 @@ static u8 *prev_update(const tal_t *ctx, chan->half[direction].bcast.index)); /* If it's a private update, unwrap */ - if (!fromwire_gossip_store_private_update(ctx, prev, &prev)) + if (!fromwire_gossip_store_private_update_obs(ctx, prev, &prev)) tal_steal(ctx, prev); return prev; } diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 5130cc6bf617..ed33de947db9 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 12 */ -#define GOSSIP_STORE_VER ((0 << 5) | 12) +/* We write it as major version 0, minor version 13 */ +#define GOSSIP_STORE_VER ((0 << 5) | 13) struct gossip_store { /* This is false when we're loading */ @@ -107,25 +107,85 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, * v10 removed any remaining non-htlc-max channel_update. * v11 mandated channel_updates use the htlc_maximum_msat field * v12 added the zombie flag for expired channel updates + * v13 removed private gossip entries */ static bool can_upgrade(u8 oldversion) { - return oldversion >= 9 && oldversion <= 11; + return oldversion >= 9 && oldversion <= 12; +} + +/* On upgrade, do best effort on private channels: hand them to + * lightningd as if we just receive them, before removing from the + * store */ +static void give_lightningd_canned_private_update(struct routing_state *rstate, + const u8 *msg) +{ + u8 *update; + secp256k1_ecdsa_signature signature; + struct bitcoin_blkid chain_hash; + struct short_channel_id short_channel_id; + u32 timestamp; + u8 message_flags, channel_flags; + u16 cltv_expiry_delta; + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; + u32 fee_base_msat, fee_proportional_millionths; + + if (!fromwire_gossip_store_private_update_obs(tmpctx, msg, &update)) { + status_broken("Could not parse private update %s", + tal_hex(tmpctx, msg)); + return; + } + if (!fromwire_channel_update(update, + &signature, + &chain_hash, + &short_channel_id, + ×tamp, + &message_flags, + &channel_flags, + &cltv_expiry_delta, + &htlc_minimum_msat, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_maximum_msat)) { + status_broken("Could not parse inner private update %s", + tal_hex(tmpctx, msg)); + return; + } + + /* From NULL source (i.e. trust us!) */ + tell_lightningd_peer_update(rstate, + NULL, + short_channel_id, + fee_base_msat, + fee_proportional_millionths, + cltv_expiry_delta, + htlc_minimum_msat, + htlc_maximum_msat); } static bool upgrade_field(u8 oldversion, struct routing_state *rstate, u8 **msg) { + int type = fromwire_peektype(*msg); assert(can_upgrade(oldversion)); - if (oldversion == 10) { + if (oldversion <= 10) { /* Remove old channel_update with no htlc_maximum_msat */ - if (fromwire_peektype(*msg) == WIRE_CHANNEL_UPDATE + if (type == WIRE_CHANNEL_UPDATE && tal_bytelen(*msg) == 130) { *msg = tal_free(*msg); } } + if (oldversion <= 12) { + /* Remove private entries */ + if (type == WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS) { + *msg = tal_free(*msg); + } else if (type == WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS) { + give_lightningd_canned_private_update(rstate, *msg); + *msg = tal_free(*msg); + } + } return true; } @@ -468,9 +528,7 @@ bool gossip_store_compact(struct gossip_store *gs) goto unlink_disable; /* We track location of all these message types. */ - if (msgtype == WIRE_GOSSIP_STORE_PRIVATE_CHANNEL - || msgtype == WIRE_GOSSIP_STORE_PRIVATE_UPDATE - || msgtype == WIRE_CHANNEL_ANNOUNCEMENT + if (msgtype == WIRE_CHANNEL_ANNOUNCEMENT || msgtype == WIRE_CHANNEL_UPDATE || msgtype == WIRE_NODE_ANNOUNCEMENT) { omap = tal(offmap, struct offset_map); @@ -579,7 +637,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); + const u8 *pupdate = towire_gossip_store_private_update_obs(tmpctx, update); return gossip_store_add(gs, pupdate, 0, false, false, false, NULL); } @@ -785,7 +843,7 @@ const u8 *gossip_store_get_private_update(const tal_t *ctx, const u8 *pmsg = gossip_store_get(tmpctx, gs, offset); u8 *msg; - if (!fromwire_gossip_store_private_update(ctx, pmsg, &msg)) + if (!fromwire_gossip_store_private_update_obs(ctx, pmsg, &msg)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Failed to decode private update @%"PRIu64": %s", offset, tal_hex(tmpctx, pmsg)); @@ -845,25 +903,6 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) spam = (be16_to_cpu(hdr.flags) & GOSSIP_STORE_RATELIMIT_BIT); switch (fromwire_peektype(msg)) { - case WIRE_GOSSIP_STORE_PRIVATE_CHANNEL: { - u8 *priv_chan_ann; - struct amount_sat sat; - if (!fromwire_gossip_store_private_channel(msg, msg, - &sat, - &priv_chan_ann)) { - bad = "Bad private_channel"; - goto badmsg; - } - - if (!routing_add_private_channel(rstate, NULL, - sat, priv_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)) { @@ -904,12 +943,6 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) 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"; - goto badmsg; - } - /* fall thru */ case WIRE_CHANNEL_UPDATE: if (!routing_add_channel_update(rstate, take(msg), gs->len, @@ -930,6 +963,10 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) } stats[2]++; break; + /* FIXME: Don't actually put these in! */ + case WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS: + case WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS: + break; default: bad = "Unknown message"; goto badmsg; diff --git a/gossipd/gossip_store_wire.csv b/gossipd/gossip_store_wire.csv index 55e62c3bd61f..b0f81c371293 100644 --- a/gossipd/gossip_store_wire.csv +++ b/gossipd/gossip_store_wire.csv @@ -9,14 +9,14 @@ msgtype,gossip_store_channel_amount,4101 msgdata,gossip_store_channel_amount,satoshis,amount_sat, # Mimics a channel_announce, except signatures are all-zero -msgtype,gossip_store_private_channel,4104 -msgdata,gossip_store_private_channel,satoshis,amount_sat, -msgdata,gossip_store_private_channel,len,u16, -msgdata,gossip_store_private_channel,announcement,u8,len +msgtype,gossip_store_private_channel_obs,4104 +msgdata,gossip_store_private_channel_obs,satoshis,amount_sat, +msgdata,gossip_store_private_channel_obs,len,u16, +msgdata,gossip_store_private_channel_obs,announcement,u8,len -msgtype,gossip_store_private_update,4102 -msgdata,gossip_store_private_update,len,u16, -msgdata,gossip_store_private_update,update,u8,len +msgtype,gossip_store_private_update_obs,4102 +msgdata,gossip_store_private_update_obs,len,u16, +msgdata,gossip_store_private_update_obs,update,u8,len msgtype,gossip_store_delete_chan,4103 msgdata,gossip_store_delete_chan,scid,short_channel_id, diff --git a/gossipd/routing.c b/gossipd/routing.c index a236a102a532..47b9aa8005fc 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -883,8 +883,8 @@ static void delete_chan_messages_from_store(struct routing_state *rstate, 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; + update_type = WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS; + announcment_type = WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS; } /* If these aren't in the store, these are noops. */ @@ -1333,7 +1333,7 @@ static void delete_spam_update(struct routing_state *rstate, gossip_store_delete(rstate->gs, &hc->rgraph, update_is_public ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); hc->rgraph.index = hc->bcast.index; hc->rgraph.timestamp = hc->bcast.timestamp; } @@ -1348,14 +1348,14 @@ static bool is_chan_dying(struct routing_state *rstate, return false; } -static void tell_lightningd_peer_update(struct routing_state *rstate, - const struct node_id *source_peer, - struct short_channel_id scid, - u32 fee_base_msat, - u32 fee_ppm, - u16 cltv_delta, - struct amount_msat htlc_minimum, - struct amount_msat htlc_maximum) +void tell_lightningd_peer_update(struct routing_state *rstate, + const struct node_id *source_peer, + struct short_channel_id scid, + u32 fee_base_msat, + u32 fee_ppm, + u16 cltv_delta, + struct amount_msat htlc_minimum, + struct amount_msat htlc_maximum) { struct peer_update remote_update; u8* msg; @@ -1532,7 +1532,7 @@ bool routing_add_channel_update(struct routing_state *rstate, gossip_store_delete(rstate->gs, &hc->bcast, is_chan_public(chan) ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); /* Update timestamp(s) */ hc->rgraph.timestamp = timestamp; @@ -1609,7 +1609,7 @@ bool routing_add_channel_update(struct routing_state *rstate, gossip_store_delete(rstate->gs, &chan->bcast, is_chan_public(chan) ? WIRE_CHANNEL_ANNOUNCEMENT - : WIRE_GOSSIP_STORE_PRIVATE_CHANNEL); + : WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS); chan->bcast.index = gossip_store_add(rstate->gs, zombie_announcement, chan->bcast.timestamp, @@ -1627,12 +1627,12 @@ bool routing_add_channel_update(struct routing_state *rstate, gossip_store_delete(rstate->gs, &chan->half[!direction].rgraph, is_chan_public(chan) ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); } gossip_store_delete(rstate->gs, &chan->half[!direction].bcast, is_chan_public(chan) ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); chan->half[!direction].bcast.index = gossip_store_add(rstate->gs, zombie_update[0], chan->half[!direction].bcast.timestamp, @@ -2219,7 +2219,7 @@ bool routing_add_private_channel(struct routing_state *rstate, /* Create new (unannounced) channel */ chan = new_chan(rstate, &scid, &node_id[0], &node_id[1], capacity); if (!index) { - u8 *msg = towire_gossip_store_private_channel(tmpctx, + u8 *msg = towire_gossip_store_private_channel_obs(tmpctx, capacity, chan_ann); index = gossip_store_add(rstate->gs, msg, 0, false, false, false, diff --git a/gossipd/routing.h b/gossipd/routing.h index b2c2dd5f323d..baef0d11698c 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -435,4 +435,14 @@ bool node_has_broadcastable_channels(const struct node *node); const char *unfinalized_entries(const tal_t *ctx, struct routing_state *rstate); void remove_all_gossip(struct routing_state *rstate); + +/* We have an update for one of our channels (or unknown). */ +void tell_lightningd_peer_update(struct routing_state *rstate, + const struct node_id *source_peer, + struct short_channel_id scid, + u32 fee_base_msat, + u32 fee_ppm, + u16 cltv_delta, + struct amount_msat htlc_minimum, + struct amount_msat htlc_maximum); #endif /* LIGHTNING_GOSSIPD_ROUTING_H */ diff --git a/plugins/sql.c b/plugins/sql.c index b56b454795ae..b427d876f6d3 100644 --- a/plugins/sql.c +++ b/plugins/sql.c @@ -743,7 +743,7 @@ static bool extract_scid(int gosstore_fd, size_t off, u16 type, /* For delete_chan scid immediately follows type */ if (type == WIRE_GOSSIP_STORE_DELETE_CHAN) off += 2; - else if (type == WIRE_GOSSIP_STORE_PRIVATE_UPDATE) + else if (type == WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS) /* Prepend header */ off += 2 + 2 + update_scid_off; else if (type == WIRE_CHANNEL_UPDATE) @@ -841,7 +841,7 @@ static struct command_result *channels_refresh(struct command *cmd, /* If we see a channel_announcement, we don't care until we * see the channel_update */ if (type == WIRE_CHANNEL_UPDATE - || type == WIRE_GOSSIP_STORE_PRIVATE_UPDATE) { + || type == WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS) { /* This can fail if entry not fully written yet. */ if (!extract_scid(gosstore_fd, off, type, &scid)) { gosstore_channels_off = off; From 422ea4c9347949898cd145ca43454e3c3c187bd6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:16 +1030 Subject: [PATCH 05/38] gossipd: get told about new block before splicing code tries to feed us channel_announcement. ``` lightningd-2 2023-11-21T01:02:31.375Z DEBUG gossipd: Ignoring future channel_announcment for 109x1x0 (current block 108) lightningd-2 2023-11-21T01:02:31.375Z DEBUG gossipd: REPLY WIRE_GOSSIPD_ADDGOSSIP_REPLY with 0 fds lightningd-2 2023-11-21T01:02:31.376Z DEBUG gossipd: Bad gossip order: WIRE_CHANNEL_UPDATE before announcement 109x1x0/0 from local lightningd-2 2023-11-21T01:02:31.376Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-hsmd: Got WIRE_HSMD_CHECK_OUTPOINT lightningd-2 2023-11-21T01:02:31.376Z DEBUG gossipd: REPLY WIRE_GOSSIPD_ADDGOSSIP_REPLY with 0 fds lightningd-2 2023-11-21T01:02:31.376Z DEBUG hsmd: Client: Received message 32 from client lightningd-2 2023-11-21T01:02:31.376Z DEBUG gossipd: REPLY WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY with 0 fd s ``` 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 9b67077c7c43..4b127999b8ee 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -854,12 +854,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 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); + /* Tell watch code to re-evaluate all txs. */ + watch_topology_changed(topo); + /* Maybe need to rebroadcast. */ rebroadcast_txs(topo); From fc9ec4b239d923b1c0c64002570558502c1527f3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 06/38] dualopend: tell lightningd what remote alias the peer sent. Signed-off-by: Rusty Russell --- lightningd/channel_control.c | 17 ++++++++++++----- lightningd/channel_control.h | 3 ++- lightningd/dual_open_control.c | 7 +++++-- openingd/dualopend.c | 2 +- openingd/dualopend_wire.csv | 1 + 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 3beb7a20181c..cd68fcd1dbbd 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -936,7 +936,8 @@ void lockin_complete(struct channel *channel, } bool channel_on_channel_ready(struct channel *channel, - struct pubkey *next_per_commitment_point) + const struct pubkey *next_per_commitment_point, + const struct short_channel_id *remote_alias) { if (channel->remote_channel_ready) { channel_internal_error(channel, @@ -945,6 +946,13 @@ bool channel_on_channel_ready(struct channel *channel, } update_per_commit_point(channel, next_per_commitment_point); + /* FIXME: we should apply this even if it changed! */ + if (channel->alias[REMOTE] == NULL) { + channel->alias[REMOTE] + = tal_dup_or_null(channel, struct short_channel_id, + remote_alias); + } + log_debug(channel->log, "Got channel_ready"); channel->remote_channel_ready = true; @@ -1034,12 +1042,11 @@ static void peer_got_channel_ready(struct channel *channel, const u8 *msg) return; } - if (!channel_on_channel_ready(channel, &next_per_commitment_point)) + if (!channel_on_channel_ready(channel, + &next_per_commitment_point, + alias_remote)) 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); diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index d8c52bdb1141..b0dbb075856b 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -33,7 +33,8 @@ struct command_result *cancel_channel_before_broadcast(struct command *cmd, /* Update the channel info on channel_ready */ bool channel_on_channel_ready(struct channel *channel, - struct pubkey *next_per_commitment_point); + const struct pubkey *next_per_commitment_point, + const struct short_channel_id *remote_alias); /* 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 3fe3fe0ede73..4f752ce66efc 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1940,8 +1940,11 @@ static void handle_peer_locked(struct subd *dualopend, const u8 *msg) { struct pubkey remote_per_commit; struct channel *channel = dualopend->channel; + struct short_channel_id *remote_alias; - if (!fromwire_dualopend_peer_locked(msg, &remote_per_commit)) { + if (!fromwire_dualopend_peer_locked(msg, msg, + &remote_per_commit, + &remote_alias)) { channel_internal_error(channel, "Bad WIRE_DUALOPEND_PEER_LOCKED: %s", tal_hex(msg, msg)); @@ -1950,7 +1953,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_channel_ready(channel, &remote_per_commit)) + if (!channel_on_channel_ready(channel, &remote_per_commit, remote_alias)) return; /* Remember that we got the lock-in */ diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 1638c3539c82..125d428521fb 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1563,7 +1563,7 @@ static u8 *handle_channel_ready(struct state *state, u8 *msg) /* We save when the peer locks, so we do the right * thing on reconnects */ if (!state->channel_ready[REMOTE]) { - msg = towire_dualopend_peer_locked(NULL, &remote_per_commit); + msg = towire_dualopend_peer_locked(NULL, &remote_per_commit, tlvs->short_channel_id); wire_sync_write(REQ_FD, take(msg)); } diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 5efbac7865a2..f714fe0b2e91 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -222,6 +222,7 @@ msgtype,dualopend_tx_sigs_sent,7012 # dualopend->peer peer locked channel msgtype,dualopend_peer_locked,7018 msgdata,dualopend_peer_locked,remote_per_commit,pubkey, +msgdata,dualopend_peer_locked,remote_alias,?short_channel_id, # dualopend->master this channel has been locked msgtype,dualopend_channel_locked,7019 From 55d407d57960c1495bb1d329b8b3e66992644443 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 07/38] lightningd: set channel's local alias at init. Rather than having channeld/dualopend do it, we can set it and tell them. Signed-off-by: Rusty Russell --- channeld/channeld.c | 18 +++++++++++------- channeld/channeld_wire.csv | 3 +-- lightningd/channel.c | 10 +++++++--- lightningd/channel_control.c | 8 ++++---- lightningd/dual_open_control.c | 11 +++-------- lightningd/opening_control.c | 8 ++++---- openingd/dualopend.c | 8 +++++++- openingd/dualopend_wire.csv | 2 ++ 8 files changed, 39 insertions(+), 29 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 2cb2f198b6b3..c7b8335e0839 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -139,6 +139,9 @@ struct peer { /* Which direction of the channel do we control? */ u16 channel_direction; + /* Local scid alias */ + struct short_channel_id local_alias; + /* The scriptpubkey to use for shutting down. */ u32 *final_index; struct ext_key *final_ext_key; @@ -5272,6 +5275,7 @@ static void peer_reconnect(struct peer *peer, && next_commitment_number == 1) { struct tlv_channel_ready_tlvs *tlvs = tlv_channel_ready_tlvs_new(tmpctx); + tlvs->short_channel_id = &peer->local_alias; 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 */ @@ -5554,7 +5558,7 @@ 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, *alias_local; + struct short_channel_id *scid; struct tlv_channel_ready_tlvs *tlvs; struct pubkey point; bool splicing; @@ -5563,7 +5567,6 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) if (!fromwire_channeld_funding_depth(tmpctx, msg, &scid, - &alias_local, &depth, &splicing, &txid)) @@ -5594,15 +5597,15 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) status_debug("handle_funding_depth: Setting short_channel_ids[LOCAL] to %s", type_to_string(tmpctx, struct short_channel_id, - (scid ? scid : alias_local))); + (scid ? scid : &peer->local_alias))); /* 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; + else + peer->short_channel_ids[LOCAL] = peer->local_alias; } if (!peer->channel_ready[LOCAL]) { @@ -5612,7 +5615,7 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) type_to_string(tmpctx, struct pubkey, &peer->next_local_per_commit)); tlvs = tlv_channel_ready_tlvs_new(tmpctx); - tlvs->short_channel_id = alias_local; + tlvs->short_channel_id = &peer->local_alias; /* Need to retrieve the first point again, even if we * moved on, as channel_ready explicitly includes the @@ -6166,7 +6169,8 @@ static void init_channel(struct peer *peer) &pbases, &reestablish_only, &peer->experimental_upgrade, - &peer->splice_state->inflights)) { + &peer->splice_state->inflights, + &peer->local_alias)) { master_badmsg(WIRE_CHANNELD_INIT, msg); } diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index fcc76b5ff8a0..902d475aee0f 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -81,13 +81,12 @@ msgdata,channeld_init,reestablish_only,bool, msgdata,channeld_init,experimental_upgrade,bool, msgdata,channeld_init,num_inflights,u16, msgdata,channeld_init,inflights,inflight,num_inflights +msgdata,channeld_init,scid_alias,short_channel_id, # 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, msgdata,channeld_funding_depth,splicing,bool, msgdata,channeld_funding_depth,txid,bitcoin_txid, diff --git a/lightningd/channel.c b/lightningd/channel.c index f180e5882a7b..204d49297469 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -278,7 +279,10 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->closing_feerate_range = NULL; channel->peer_update = NULL; channel->channel_update = NULL; - channel->alias[LOCAL] = channel->alias[REMOTE] = NULL; + channel->alias[REMOTE] = NULL; + /* We don't even bother checking for clashes. */ + channel->alias[LOCAL] = tal(channel, struct short_channel_id); + randombytes_buf(channel->alias[LOCAL], sizeof(struct short_channel_id)); channel->shutdown_scriptpubkey[REMOTE] = NULL; channel->last_was_revoke = false; @@ -397,7 +401,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, bool remote_channel_ready, /* NULL or stolen */ struct short_channel_id *scid, - struct short_channel_id *alias_local STEALS, + struct short_channel_id *alias_local TAKES, struct short_channel_id *alias_remote STEALS, struct channel_id *cid, struct amount_msat our_msat, @@ -508,7 +512,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->our_funds = our_funds; channel->remote_channel_ready = remote_channel_ready; channel->scid = tal_steal(channel, scid); - channel->alias[LOCAL] = tal_steal(channel, alias_local); + channel->alias[LOCAL] = tal_dup_or_null(channel, struct short_channel_id, alias_local); channel->alias[REMOTE] = tal_steal(channel, alias_remote); /* Haven't gotten one yet. */ channel->cid = *cid; channel->our_msat = our_msat; diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index cd68fcd1dbbd..2da3eef60ac7 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -594,7 +594,6 @@ static enum watch_result splice_depth_cb(struct lightningd *ld, subd_send_msg(inflight->channel->owner, take(towire_channeld_funding_depth( NULL, &scid, - inflight->channel->alias[LOCAL], depth, true, txid))); } @@ -1700,7 +1699,8 @@ bool peer_start_channeld(struct channel *channel, reestablish_only, ld->experimental_upgrade_protocol, cast_const2(const struct inflight **, - inflights)); + inflights), + channel->alias[LOCAL]); /* We don't expect a response: we are triggered by funding_depth_cb. */ subd_send_msg(channel->owner, take(initmsg)); @@ -1719,7 +1719,7 @@ bool peer_start_channeld(struct channel *channel, /* Artificial confirmation event for zeroconf */ subd_send_msg(channel->owner, take(towire_channeld_funding_depth( - NULL, channel->scid, channel->alias[LOCAL], 0, false, + NULL, channel->scid, 0, false, &txid))); return true; } @@ -1742,7 +1742,7 @@ void channeld_tell_depth(struct channel *channel, subd_send_msg(channel->owner, take(towire_channeld_funding_depth( - NULL, channel->scid, channel->alias[LOCAL], depth, + NULL, channel->scid, depth, false, txid))); } diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 4f752ce66efc..17f25fa308f7 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -33,7 +33,6 @@ #include #include #include -#include struct commit_rcvd { struct channel *channel; @@ -1465,12 +1464,6 @@ wallet_commit_channel(struct lightningd *ld, = p2tr_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); @@ -4055,6 +4048,7 @@ bool peer_start_dualopend(struct peer *peer, &channel->local_funding_pubkey, channel->minimum_depth, peer->ld->config.require_confirmed_inputs, + channel->alias[LOCAL], peer->ld->dev_any_channel_type); subd_send_msg(channel->owner, take(msg)); return true; @@ -4168,7 +4162,8 @@ bool peer_restart_dualopend(struct peer *peer, NULL : &inflight->lease_amt, channel->type, channel->req_confirmed_ins[LOCAL], - channel->req_confirmed_ins[REMOTE]); + channel->req_confirmed_ins[REMOTE], + channel->alias[LOCAL]); subd_send_msg(channel->owner, take(msg)); return true; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index d7bcde5eb652..c13efb4226d5 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -112,7 +112,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; + struct short_channel_id local_alias; struct timeabs timestamp; bool any_active = peer_any_channel(uc->peer, channel_state_wants_peercomms, NULL); @@ -174,8 +174,8 @@ 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)); + /* This won't clash, we don't even bother checking */ + randombytes_buf(&local_alias, sizeof(local_alias)); channel = new_channel(uc->peer, uc->dbid, NULL, /* No shachain yet */ @@ -194,7 +194,7 @@ wallet_commit_channel(struct lightningd *ld, local_funding, false, /* !remote_channel_ready */ NULL, /* no scid yet */ - alias_local, /* But maybe we have an alias we want to use? */ + &local_alias, NULL, /* They haven't told us an alias yet */ cid, /* The three arguments below are msatoshi_to_us, diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 125d428521fb..407105967079 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -227,6 +227,9 @@ struct state { /* Does this negotation require confirmed inputs? */ bool require_confirmed_inputs[NUM_SIDES]; + /* Our alias */ + struct short_channel_id local_alias; + bool dev_accept_any_channel_type; }; @@ -3869,6 +3872,7 @@ static void send_channel_ready(struct state *state) /* Figure out the next local commit */ hsm_per_commitment_point(1, &next_local_per_commit); + tlvs->short_channel_id = &state->local_alias; msg = towire_channel_ready(NULL, &state->channel_id, &next_local_per_commit, tlvs); peer_write(state->pps, take(msg)); @@ -4382,6 +4386,7 @@ int main(int argc, char *argv[]) &state->our_funding_pubkey, &state->minimum_depth, &state->require_confirmed_inputs[LOCAL], + &state->local_alias, &state->dev_accept_any_channel_type)) { /*~ Initially we're not associated with a channel, but * handle_peer_gossip_or_error compares this. */ @@ -4446,7 +4451,8 @@ int main(int argc, char *argv[]) &requested_lease, &state->channel_type, &state->require_confirmed_inputs[LOCAL], - &state->require_confirmed_inputs[REMOTE])) { + &state->require_confirmed_inputs[REMOTE], + &state->local_alias)) { bool ok; diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index f714fe0b2e91..4a7f8a4803b4 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -29,6 +29,7 @@ msgdata,dualopend_init,our_funding_pubkey,pubkey, # Constraints in case the other end tries to open a channel. msgdata,dualopend_init,minimum_depth,u32, msgdata,dualopend_init,require_confirmed_inputs,bool, +msgdata,dualopend_init,local_alias,short_channel_id, msgdata,dualopend_init,dev_accept_any_channel_type,bool, # master-dualopend: peer has reconnected @@ -77,6 +78,7 @@ msgdata,dualopend_reinit,requested_lease,?amount_sat, msgdata,dualopend_reinit,channel_type,channel_type, msgdata,dualopend_reinit,we_require_confirmed_inputs,bool, msgdata,dualopend_reinit,they_require_confirmed_inputs,bool, +msgdata,dualopend_reinit,local_alias,short_channel_id, # dualopend->master: they offered channel, should we continue? msgtype,dualopend_got_offer,7005 From ab1e783412d79535b7b9394e10a1172ac08cd4e6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 08/38] connectd: separate routine to inject message without closing connection. We will want this to send private channel_updates direct to peer. Signed-off-by: Rusty Russell --- connectd/connectd.c | 18 ++++----- connectd/connectd_wire.csv | 12 +++--- connectd/multiplex.c | 9 +---- connectd/multiplex.h | 4 -- lightningd/channel_control.c | 12 ++++-- lightningd/connect_control.c | 2 +- lightningd/dual_open_control.c | 12 ++++-- lightningd/peer_control.c | 42 ++++++++++++++------- lightningd/test/run-invoice-select-inchan.c | 9 +++-- wallet/test/run-wallet.c | 9 +++-- 10 files changed, 75 insertions(+), 54 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 6e1739410fc3..19ea0944aaa0 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1835,24 +1835,24 @@ static void start_shutdown(struct daemon *daemon, const u8 *msg) take(towire_connectd_start_shutdown_reply(NULL))); } -/* lightningd tells us to send a msg and disconnect. */ -static void peer_final_msg(struct io_conn *conn, +/* lightningd tells us to send a msg. */ +static void peer_send_msg(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { struct peer *peer; struct node_id id; u64 counter; - u8 *finalmsg; + u8 *sendmsg; - if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &counter, - &finalmsg)) - master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); + if (!fromwire_connectd_peer_send_msg(tmpctx, msg, &id, &counter, + &sendmsg)) + master_badmsg(WIRE_CONNECTD_PEER_SEND_MSG, msg); /* 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) - multiplex_final_msg(peer, take(finalmsg)); + inject_peer_msg(peer, take(sendmsg)); } static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) @@ -2078,8 +2078,8 @@ static struct io_plan *recv_req(struct io_conn *conn, peer_discard(daemon, msg); goto out; - case WIRE_CONNECTD_PEER_FINAL_MSG: - peer_final_msg(conn, daemon, msg); + case WIRE_CONNECTD_PEER_SEND_MSG: + peer_send_msg(conn, daemon, msg); goto out; case WIRE_CONNECTD_PING: diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 6b8f9e27d6a0..db895d6c5d08 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -99,12 +99,12 @@ 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 +# master -> connectd: give message to peer. +msgtype,connectd_peer_send_msg,2003 +msgdata,connectd_peer_send_msg,id,node_id, +msgdata,connectd_peer_send_msg,counter,u64, +msgdata,connectd_peer_send_msg,len,u16, +msgdata,connectd_peer_send_msg,msg,u8,len # master -> connectd: do you have a memleak? msgtype,connectd_dev_memleak,2033 diff --git a/connectd/multiplex.c b/connectd/multiplex.c index a3b9e2d08258..3c50a2117ddd 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -167,12 +167,6 @@ void inject_peer_msg(struct peer *peer, const u8 *msg TAKES) 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, ...) { @@ -187,7 +181,8 @@ static void send_warning(struct peer *peer, const char *fmt, ...) msg = towire_warningfmtv(NULL, NULL, fmt, ap); va_end(ap); - multiplex_final_msg(peer, take(msg)); + inject_peer_msg(peer, take(msg)); + drain_peer(peer); } /* Kicks off write_to_peer() to look for more gossip to send from store */ diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 489ab4653825..5988049c7538 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -14,10 +14,6 @@ struct feature_set; 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); - /* Inject a message into the output stream. Unlike a raw msg_enqueue, * this does io logging. */ void inject_peer_msg(struct peer *peer, const u8 *msg TAKES); diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 2da3eef60ac7..d83c1ffa1db5 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1108,10 +1108,14 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg) /* Get connectd to send warning, and then allow reconnect. */ subd_send_msg(ld->connectd, - take(towire_connectd_peer_final_msg(NULL, - &channel->peer->id, - channel->peer->connectd_counter, - warning))); + take(towire_connectd_peer_send_msg(NULL, + &channel->peer->id, + channel->peer->connectd_counter, + warning))); + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, + &channel->peer->id, + channel->peer->connectd_counter))); channel_fail_transient(channel, true, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); return; diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index ecb685bdd375..192f3de8f696 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -578,7 +578,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_DEV_REPORT_FDS: - case WIRE_CONNECTD_PEER_FINAL_MSG: + case WIRE_CONNECTD_PEER_SEND_MSG: case WIRE_CONNECTD_PEER_CONNECT_SUBD: case WIRE_CONNECTD_PING: case WIRE_CONNECTD_SEND_ONIONMSG: diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 17f25fa308f7..79bb9f91b4c1 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1576,10 +1576,14 @@ static void handle_peer_wants_to_close(struct subd *dualopend, /* 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))); + take(towire_connectd_peer_send_msg(NULL, + &channel->peer->id, + channel->peer->connectd_counter, + warning))); + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, + &channel->peer->id, + channel->peer->connectd_counter))); channel_fail_transient(channel, true, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); return; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 64753c0f13bf..21bdcac929d1 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1318,9 +1318,13 @@ static void connect_activate_subd(struct lightningd *ld, struct channel *channel 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, - channel->peer->connectd_counter, - error))); + take(towire_connectd_peer_send_msg(NULL, &channel->peer->id, + channel->peer->connectd_counter, + error))); + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, + &channel->peer->id, + channel->peer->connectd_counter))); } static void peer_connected_hook_final(struct peer_connected_hook_payload *payload STEALS) @@ -1367,12 +1371,16 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa /* Developer hack to fail all channels on permfail line. */ if (dev_disconnect_permanent(ld)) { list_for_each(&peer->channels, channel, list) { + subd_send_msg(ld->connectd, + take(towire_connectd_peer_send_msg(NULL, &peer->id, + peer->connectd_counter, + channel->error))); + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, + &peer->id, + peer->connectd_counter))); channel_fail_permanent(channel, REASON_LOCAL, "dev_disconnect permfail"); - subd_send_msg(ld->connectd, - take(towire_connectd_peer_final_msg(NULL, &peer->id, - peer->connectd_counter, - channel->error))); } return; } @@ -1394,9 +1402,13 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa 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, - peer->connectd_counter, - error))); + take(towire_connectd_peer_send_msg(NULL, &peer->id, + peer->connectd_counter, + error))); + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, + &peer->id, + peer->connectd_counter))); } static bool @@ -1764,9 +1776,13 @@ void peer_spoke(struct lightningd *ld, const u8 *msg) 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, - peer->connectd_counter, - error))); + take(towire_connectd_peer_send_msg(NULL, &peer->id, + peer->connectd_counter, + error))); + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, + &peer->id, + peer->connectd_counter))); return; tell_connectd: diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index afbe35775e30..09991355aa7b 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -930,12 +930,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, 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(); } -/* 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, u64 counter UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_send_msg */ +u8 *towire_connectd_peer_send_msg(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_send_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(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index b7a87c10d3ac..8860e184cea5 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -993,12 +993,15 @@ 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(); } -/* 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, u64 counter UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_send_msg */ +u8 *towire_connectd_peer_send_msg(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_send_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(); } From 0f4c1410f8cf9ac111f0afca1c2c9bae59888a76 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 09/38] channeld: tell lightningd scid from `announcement_signatures` message. Signed-off-by: Rusty Russell --- channeld/channeld.c | 1 + channeld/channeld_wire.csv | 1 + lightningd/channel_control.c | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index c7b8335e0839..0b9fe2bb46d1 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -660,6 +660,7 @@ static bool channel_announcement_negotiate(struct peer *peer) * announcement to MASTER. */ wire_sync_write(MASTER_FD, take(towire_channeld_got_announcement(NULL, + &peer->short_channel_ids[REMOTE], &peer->announcement_node_sigs[REMOTE], &peer->announcement_bitcoin_sigs[REMOTE]))); diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 902d475aee0f..b8bbb7ae17e7 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -323,6 +323,7 @@ msgdata,channeld_fail_fallen_behind,remote_per_commitment_point,?pubkey, # When we receive announcement_signatures for channel announce msgtype,channeld_got_announcement,1017 +msgdata,channeld_got_announcement,scid,short_channel_id, msgdata,channeld_got_announcement,remote_ann_node_sig,secp256k1_ecdsa_signature, msgdata,channeld_got_announcement,remote_ann_bitcoin_sig,secp256k1_ecdsa_signature, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index d83c1ffa1db5..a931fd02b55e 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1057,10 +1057,12 @@ static void peer_got_announcement(struct channel *channel, const u8 *msg) { secp256k1_ecdsa_signature remote_ann_node_sig; secp256k1_ecdsa_signature remote_ann_bitcoin_sig; + struct short_channel_id unused; if (!fromwire_channeld_got_announcement(msg, - &remote_ann_node_sig, - &remote_ann_bitcoin_sig)) { + &unused, + &remote_ann_node_sig, + &remote_ann_bitcoin_sig)) { channel_internal_error(channel, "bad channel_got_announcement %s", tal_hex(tmpctx, msg)); From 43b731eaa6dc2199d6b849f826b378e8c4ca854a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 10/38] lightningd: tweak db remote channel_announcement sigs API. Don't return false on db errors (we always fail on those), but return false if they don't exist. Also, add routine to clear them. Signed-off-by: Rusty Russell --- lightningd/channel_control.c | 17 +++++------ wallet/test/run-wallet.c | 6 +++- wallet/wallet.c | 55 ++++++++++++++++++++---------------- wallet/wallet.h | 20 +++++++++---- 4 files changed, 59 insertions(+), 39 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index a931fd02b55e..b2b04e3a2eb8 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1557,14 +1557,15 @@ bool peer_start_channeld(struct channel *channel, if (channel->ignore_fee_limits || ld->config.ignore_fee_limits) log_unusual(channel->log, "Ignoring fee limits!"); - if (!wallet_remote_ann_sigs_load(tmpctx, channel->peer->ld->wallet, - channel->dbid, - &remote_ann_node_sig, - &remote_ann_bitcoin_sig)) { - channel_internal_error(channel, - "Could not load remote announcement" - " signatures"); - return false; + remote_ann_node_sig = tal(tmpctx, secp256k1_ecdsa_signature); + remote_ann_bitcoin_sig = tal(tmpctx, secp256k1_ecdsa_signature); + + if (!wallet_remote_ann_sigs_load(channel->peer->ld->wallet, + channel, + remote_ann_node_sig, + remote_ann_bitcoin_sig)) { + remote_ann_node_sig = tal_free(remote_ann_node_sig); + remote_ann_bitcoin_sig = tal_free(remote_ann_bitcoin_sig); } pbases = wallet_penalty_base_load_for_channel( diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 8860e184cea5..ebc7e4ce3af3 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1818,12 +1818,16 @@ static bool test_channel_crud(struct lightningd *ld, const tal_t *ctx) wallet_announcement_save(w, c1.dbid, node_sig1, bitcoin_sig1); CHECK_MSG(!wallet_err, tal_fmt(w, "Insert ann sigs into DB: %s", wallet_err)); - CHECK_MSG(load = wallet_remote_ann_sigs_load(w, w, c1.dbid, &node_sig2, &bitcoin_sig2), tal_fmt(w, "Load ann sigs from DB")); + node_sig2 = tal(tmpctx, secp256k1_ecdsa_signature); + bitcoin_sig2 = tal(tmpctx, secp256k1_ecdsa_signature); + CHECK_MSG(load = wallet_remote_ann_sigs_load(w, &c1, node_sig2, bitcoin_sig2), tal_fmt(w, "Load ann sigs from DB")); CHECK_MSG(!wallet_err, tal_fmt(w, "Load ann sigs from DB: %s", wallet_err)); CHECK(load == true); CHECK_MSG(!memcmp(node_sig1, node_sig2, sizeof(*node_sig1)), "Compare ann sigs loaded with saved (v5)"); CHECK_MSG(!memcmp(bitcoin_sig1, bitcoin_sig2, sizeof(*node_sig1)), "Compare ann sigs loaded with saved (v5)"); + wallet_remote_ann_sigs_clear(w, &c1); + CHECK(!wallet_remote_ann_sigs_load(w, &c1, node_sig2, bitcoin_sig2)); db_commit_transaction(w->db); CHECK(!wallet_err); diff --git a/wallet/wallet.c b/wallet/wallet.c index f7af8fea48fd..afeff42f29e8 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1075,16 +1075,17 @@ wallet_htlc_sigs_load(const tal_t *ctx, struct wallet *w, u64 channelid, return htlc_sigs; } -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) +bool wallet_remote_ann_sigs_load(struct wallet *w, + const struct channel *chan, + secp256k1_ecdsa_signature *remote_ann_node_sig, + secp256k1_ecdsa_signature *remote_ann_bitcoin_sig) { struct db_stmt *stmt; bool res; stmt = db_prepare_v2( w->db, SQL("SELECT remote_ann_node_sig, remote_ann_bitcoin_sig" " FROM channels WHERE id = ?")); - db_bind_u64(stmt, id); + db_bind_u64(stmt, chan->dbid); db_query_prepared(stmt); res = db_step(stmt); @@ -1096,29 +1097,31 @@ bool wallet_remote_ann_sigs_load(const tal_t *ctx, struct wallet *w, u64 id, 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; + return false; } - /* the case left over is both sigs exist */ - *remote_ann_node_sig = tal(ctx, secp256k1_ecdsa_signature); - *remote_ann_bitcoin_sig = tal(ctx, secp256k1_ecdsa_signature); + if (!db_col_signature(stmt, "remote_ann_node_sig", remote_ann_node_sig)) + db_fatal(w->db, "Failed to decode remote_ann_node_sig for id %"PRIu64, chan->dbid); - if (!db_col_signature(stmt, "remote_ann_node_sig", *remote_ann_node_sig)) - goto fail; - - if (!db_col_signature(stmt, "remote_ann_bitcoin_sig", *remote_ann_bitcoin_sig)) - goto fail; + if (!db_col_signature(stmt, "remote_ann_bitcoin_sig", remote_ann_bitcoin_sig)) + db_fatal(w->db, "Failed to decode remote_ann_bitcoin_sig for id %"PRIu64, chan->dbid); tal_free(stmt); return true; +} -fail: - *remote_ann_node_sig = tal_free(*remote_ann_node_sig); - *remote_ann_bitcoin_sig = tal_free(*remote_ann_bitcoin_sig); - tal_free(stmt); - return false; +void wallet_remote_ann_sigs_clear(struct wallet *w, const struct channel *chan) +{ + struct db_stmt *stmt; + stmt = db_prepare_v2(w->db, + SQL("UPDATE channels" + " SET remote_ann_node_sig=?, remote_ann_bitcoin_sig=?" + " WHERE id = ?")); + db_bind_null(stmt); + db_bind_null(stmt); + db_bind_u64(stmt, chan->dbid); + db_exec_prepared_v2(take(stmt)); } static struct fee_states *wallet_channel_fee_states_load(struct wallet *w, @@ -2215,6 +2218,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) { struct db_stmt *stmt; u8 *last_sent_commit; + const struct peer_update *peer_update; assert(chan->first_blocknum); wallet_channel_config_save(w, &chan->our_config); @@ -2353,12 +2357,13 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt); db_bind_int(stmt, chan->ignore_fee_limits); - if (chan->peer_update) { - db_bind_int(stmt, chan->peer_update->fee_base); - db_bind_int(stmt, chan->peer_update->fee_ppm); - db_bind_int(stmt, chan->peer_update->cltv_delta); - db_bind_amount_msat(stmt, &chan->peer_update->htlc_minimum_msat); - db_bind_amount_msat(stmt, &chan->peer_update->htlc_maximum_msat); + peer_update = chan->peer_update; + if (peer_update) { + db_bind_int(stmt, peer_update->fee_base); + db_bind_int(stmt, peer_update->fee_ppm); + db_bind_int(stmt, peer_update->cltv_delta); + db_bind_amount_msat(stmt, &peer_update->htlc_minimum_msat); + db_bind_amount_msat(stmt, &peer_update->htlc_maximum_msat); } else { db_bind_null(stmt); db_bind_null(stmt); diff --git a/wallet/wallet.h b/wallet/wallet.h index 2e8138d5fcfb..904cd2301fac 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1262,15 +1262,25 @@ bool wallet_forward_delete(struct wallet *w, /** * Load remote_ann_node_sig and remote_ann_bitcoin_sig * - * @ctx: allocation context for the return value * @w: wallet containing the channel - * @id: channel database id + * @chan: channel (must be in db) * @remote_ann_node_sig: location to load remote_ann_node_sig to * @remote_ann_bitcoin_sig: location to load remote_ann_bitcoin_sig to + * + * Returns false if the signatures were null. + */ +bool wallet_remote_ann_sigs_load(struct wallet *w, + const struct channel *chan, + secp256k1_ecdsa_signature *remote_ann_node_sig, + secp256k1_ecdsa_signature *remote_ann_bitcoin_sig); + +/** + * Null out remote_ann_node_sig and remote_ann_bitcoin_sig + * + * @w: wallet containing the channel + * @id: channel database id */ -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); +void wallet_remote_ann_sigs_clear(struct wallet *w, const struct channel *chan); /** * Get a list of transactions that we track in the wallet. From 046139fe7f64ec7ba57b2a41b92069e94e3819e0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 11/38] lightningd: add gossip_generation.c Routines to generate our own channel_update and channel_announcement messages. Signed-off-by: Rusty Russell --- lightningd/Makefile | 1 + lightningd/gossip_generation.c | 207 +++++++++++++++++++++++++++++++++ lightningd/gossip_generation.h | 64 ++++++++++ 3 files changed, 272 insertions(+) create mode 100644 lightningd/gossip_generation.c create mode 100644 lightningd/gossip_generation.h diff --git a/lightningd/Makefile b/lightningd/Makefile index 4126c0dc7713..b320fef010fc 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -15,6 +15,7 @@ LIGHTNINGD_SRC := \ lightningd/feerate.c \ lightningd/forwards.c \ lightningd/gossip_control.c \ + lightningd/gossip_generation.c \ lightningd/hsm_control.c \ lightningd/htlc_end.c \ lightningd/htlc_set.c \ diff --git a/lightningd/gossip_generation.c b/lightningd/gossip_generation.c new file mode 100644 index 000000000000..ef9aee72a67c --- /dev/null +++ b/lightningd/gossip_generation.c @@ -0,0 +1,207 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Once we know which way nodes go, it's easy to construct */ +static u8 *create_channel_announcement_dir(const tal_t *ctx, + const u8 *features, + struct short_channel_id scid, + const secp256k1_ecdsa_signature node_signature[NUM_SIDES], + const secp256k1_ecdsa_signature bitcoin_signature[NUM_SIDES], + const struct node_id node_id[NUM_SIDES], + const struct pubkey funding_pubkey[NUM_SIDES]) +{ + enum side first, second; + + if (node_id_cmp(&node_id[LOCAL], &node_id[REMOTE]) < 0) + first = LOCAL; + else + first = REMOTE; + second = !first; + + return towire_channel_announcement(ctx, + &node_signature[first], + &node_signature[second], + &bitcoin_signature[first], + &bitcoin_signature[second], + features, + &chainparams->genesis_blockhash, + &scid, + &node_id[first], + &node_id[second], + &funding_pubkey[first], + &funding_pubkey[second]); +} + +static void copysig_or_zero(secp256k1_ecdsa_signature *dst, + const secp256k1_ecdsa_signature *src) +{ + if (!src) + memset(dst, 0, sizeof(*dst)); + else + *dst = *src; +} + +u8 *create_channel_announcement(const tal_t *ctx, + const struct channel *channel, + const secp256k1_ecdsa_signature *local_node_signature, + const secp256k1_ecdsa_signature *local_bitcoin_signature, + const secp256k1_ecdsa_signature *remote_node_signature, + const secp256k1_ecdsa_signature *remote_bitcoin_signature) +{ + secp256k1_ecdsa_signature node_signature[NUM_SIDES], bitcoin_signature[NUM_SIDES]; + struct node_id node_id[NUM_SIDES]; + struct pubkey funding_pubkey[NUM_SIDES]; + u8 *features; + + features = get_agreed_channelfeatures(tmpctx, channel->peer->ld->our_features, + channel->peer->their_features); + + copysig_or_zero(&bitcoin_signature[LOCAL], local_bitcoin_signature); + copysig_or_zero(&bitcoin_signature[REMOTE], remote_bitcoin_signature); + copysig_or_zero(&node_signature[LOCAL], local_node_signature); + copysig_or_zero(&node_signature[REMOTE], remote_node_signature); + node_id[LOCAL] = channel->peer->ld->id; + node_id[REMOTE] = channel->peer->id; + funding_pubkey[LOCAL] = channel->local_funding_pubkey; + funding_pubkey[REMOTE] = channel->channel_info.remote_fundingkey; + return create_channel_announcement_dir(ctx, features, *channel->scid, + node_signature, bitcoin_signature, node_id, funding_pubkey); +} + +u8 *unsigned_channel_update(const tal_t *ctx, + const struct channel *channel, + const struct short_channel_id *scid, + const u32 *old_timestamp, + bool forwardable, + bool enabled) +{ + struct lightningd *ld = channel->peer->ld; + secp256k1_ecdsa_signature dummy_sig; + u8 message_flags, channel_flags; + u32 timestamp; + + /* hsmd fills this in */ + memset(&dummy_sig, 0, sizeof(dummy_sig)); + /* 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. | + */ + channel_flags = node_id_idx(&channel->peer->ld->id, + &channel->peer->id); + if (!enabled) + channel_flags |= ROUTING_FLAGS_DISABLED; + + /* BOLT #7: + * + * The `message_flags` bitfield is used to provide additional details + * about the message: + * + * | Bit Position | Name | + * | ------------- | ---------------| + * | 0 | `must_be_one` | + * | 1 | `dont_forward` | + */ + message_flags = 1; + if (!forwardable) + message_flags |= ROUTING_OPT_DONT_FORWARD; + + /* Make sure timestamp changes! */ + timestamp = time_now().ts.tv_sec; + if (old_timestamp && timestamp <= *old_timestamp) + timestamp = *old_timestamp + 1; + + return towire_channel_update(ctx, + &dummy_sig, + &chainparams->genesis_blockhash, + scid, + timestamp, + message_flags, + channel_flags, + ld->config.cltv_expiry_delta, + channel->htlc_minimum_msat, + channel->feerate_base, + channel->feerate_ppm, + channel->htlc_maximum_msat); +} + +/* Helper to get non-signature, non-timestamp parts of (valid!) channel_update */ +static void get_cupdate_parts(const u8 *channel_update, + const u8 *parts[2], + size_t sizes[2]) +{ + /* BOLT #7: + * + * 1. type: 258 (`channel_update`) + * 2. data: + * * [`signature`:`signature`] + * * [`chain_hash`:`chain_hash`] + * * [`short_channel_id`:`short_channel_id`] + * * [`u32`:`timestamp`] + *... + */ + /* Note: 2 bytes for `type` field */ + /* We already checked it's valid before accepting */ + assert(tal_count(channel_update) > 2 + 64 + 32 + 8 + 4); + parts[0] = channel_update + 2 + 64; + sizes[0] = 32 + 8; + parts[1] = channel_update + 2 + 64 + 32 + 8 + 4; + sizes[1] = tal_count(channel_update) - (64 + 2 + 32 + 8 + 4); +} + +bool channel_update_same(const u8 *cupdate1, const u8 *cupdate2) +{ + const u8 *parts1[2], *parts2[2]; + size_t sizes1[2], sizes2[2]; + + get_cupdate_parts(cupdate1, parts1, sizes1); + get_cupdate_parts(cupdate2, parts2, sizes2); + + return memeq(parts1[0], sizes1[0], parts2[0], sizes2[0]) + && memeq(parts1[1], sizes1[1], parts2[1], sizes2[1]); +} + +bool channel_update_details(const u8 *channel_update, + u32 *timestamp, + bool *enabled) +{ + u16 cltv_expiry_delta; + struct amount_msat htlc_minimum, htlc_maximum; + u32 fee_base_msat, fee_proportional_millionths, tstamp; + u8 message_flags, channel_flags; + secp256k1_ecdsa_signature signature; + struct bitcoin_blkid chain_hash; + struct short_channel_id short_channel_id; + + if (!fromwire_channel_update(channel_update, + &signature, &chain_hash, + &short_channel_id, &tstamp, + &message_flags, &channel_flags, + &cltv_expiry_delta, + &htlc_minimum, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_maximum)) + return false; + + if (timestamp) + *timestamp = tstamp; + if (enabled) + *enabled = !(channel_flags & ROUTING_FLAGS_DISABLED); + return true; +} diff --git a/lightningd/gossip_generation.h b/lightningd/gossip_generation.h new file mode 100644 index 000000000000..675143a8dae0 --- /dev/null +++ b/lightningd/gossip_generation.h @@ -0,0 +1,64 @@ +#ifndef LIGHTNING_LIGHTNINGD_GOSSIP_GENERATION_H +#define LIGHTNING_LIGHTNINGD_GOSSIP_GENERATION_H +#include "config.h" +#include +#include + +struct channel; + +/** + * create_channel_announcement: create a channel_announcement message + * @ctx: the tal context to allocate return from + * @channel: the channel to announce + * @local_node_signature: optional local node signature + * @local_bitcoin_signature: optional local node signature + * @remote_node_signature: optional peer node signature + * @remote_bitcoin_signature: optional peer node signature + * + * The signatures are optional in case you're creating it to sign (or + * validate). + */ +u8 *create_channel_announcement(const tal_t *ctx, + const struct channel *channel, + const secp256k1_ecdsa_signature *local_node_signature, + const secp256k1_ecdsa_signature *local_bitcoin_signature, + const secp256k1_ecdsa_signature *remote_node_signature, + const secp256k1_ecdsa_signature *remote_bitcoin_signature); + +/** + * unsigned_channel_update: create a channel_update message with zeroed sig + * @ctx: the tal context to allocate return from + * @channel: the channel to announce + * @scid: the short_channel_id to sign + * @old_timestamp: optional timestamp of previous channel_update to replace. + * @forwardable: is this channel_update non-public? + * @enabled: sets channel_update's disabled flag + */ +u8 *unsigned_channel_update(const tal_t *ctx, + const struct channel *channel, + const struct short_channel_id *scid, + const u32 *old_timestamp, + bool forwardable, + bool enabled); + +/** + * channel_update_same: are these two channel updates the same? + * + * Ignoring timestamp and signatures. Basically, is it redundant? + */ +bool channel_update_same(const u8 *cupdate1, const u8 *cupdate2); + +/** + * channel_update_details: extract timestamp and/or enabled flag. + * @channel_update: the channel update, or NULL. + * @timestamp: where to extract the timestamp, or NULL. + * @enabled: where to extract the enabled flag, or NULL. + * + * Returns false (and doesn't touch @timestamp or @enabled) if + * channel_update NULL or invalid. + */ +bool channel_update_details(const u8 *channel_update, + u32 *timestamp, + bool *enabled); + +#endif /* LIGHTNING_LIGHTNINGD_GOSSIP_GENERATION_H */ From 6e05632831889879f80261267592d5653d11542a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 12/38] lightningd/channel_gossip: new file for handing and generating gossip for our channels. Signed-off-by: Rusty Russell --- lightningd/Makefile | 1 + lightningd/channel.h | 3 + lightningd/channel_gossip.c | 970 ++++++++++++++++++++++++++++++++++++ lightningd/channel_gossip.h | 55 ++ 4 files changed, 1029 insertions(+) create mode 100644 lightningd/channel_gossip.c create mode 100644 lightningd/channel_gossip.h diff --git a/lightningd/Makefile b/lightningd/Makefile index b320fef010fc..b40a7244f799 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -6,6 +6,7 @@ LIGHTNINGD_SRC := \ lightningd/chaintopology.c \ lightningd/channel.c \ lightningd/channel_control.c \ + lightningd/channel_gossip.c \ lightningd/closing_control.c \ lightningd/coin_mvts.c \ lightningd/dual_open_control.c \ diff --git a/lightningd/channel.h b/lightningd/channel.h index c2893fc09d89..578a4f762bee 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -141,6 +141,9 @@ struct channel { /* Is there a single subdaemon responsible for us? */ struct subd *owner; + /* Telling the world (or just our peer) about us */ + struct channel_gossip *channel_gossip; + /* History */ struct logger *log; struct billboard billboard; diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c new file mode 100644 index 000000000000..6ba99ecd303c --- /dev/null +++ b/lightningd/channel_gossip.c @@ -0,0 +1,970 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum channel_gossip_state { + /* Not a public channel */ + CGOSSIP_PRIVATE, + /* We can't yet send (non-forwardable) channel_update. */ + CGOSSIP_NOT_USABLE, + /* Not yet in at announcable depth. */ + CGOSSIP_NOT_DEEP_ENOUGH, + /* We want the peer's announcement_signatures. */ + CGOSSIP_NEED_PEER_SIGS, + /* We have received sigs, and announced. */ + CGOSSIP_ANNOUNCED, +}; + +struct remote_announce_sigs { + struct short_channel_id scid; + secp256k1_ecdsa_signature node_sig; + secp256k1_ecdsa_signature bitcoin_sig; +}; + +struct channel_gossip { + enum channel_gossip_state state; + + /* Cached update info */ + const u8 *cupdate; + + /* Remote channel_announcement sigs we've received (but not + * necessarily committed!) */ + struct remote_announce_sigs *remote_sigs; + + /* Timer to refresh public channels every 13 days */ + struct oneshot *refresh_timer; + + /* Details of latest channel_update sent by peer */ + const struct peer_update *peer_update; +}; + +/* FIXME: this is a hack! We should wait 1 minute after connectd has + * tried to connect to all peers. */ +static bool starting_up = true; + +/* We send non-forwardable channel updates if we can. */ +static bool can_send_channel_update(const struct channel *channel) +{ + /* Can't send if we can't talk about it. */ + if (!channel->scid && !channel->alias[REMOTE]) + return false; + if (channel_state_pre_open(channel->state)) + return false; + return true; +} + +/* We send start the channel announcement signatures if we can. + * Caller must check it's not CGOSSIP_PRIVATE, but this is used + * to set up state so we cannot assert here! + */ +static bool channel_announceable(const struct channel *channel, + u32 block_height) +{ + if (!channel->scid) + return false; + return is_scid_depth_announceable(channel->scid, block_height); +} + +static void check_channel_gossip(const struct channel *channel) +{ + struct channel_gossip *cg = channel->channel_gossip; + + /* Note: we can't assert is_scid_depth_announceable, for two reasons: + * 1. on restart and rescan, block numbers can go backwards.' + * 2. We don't get notified via channel_gossip_notify_new_block until + * there are no new blocks to add, not on every block. + */ + switch (cg->state) { + case CGOSSIP_PRIVATE: + assert(!(channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)); + assert(!cg->remote_sigs); + assert(!cg->refresh_timer); + return; + + case CGOSSIP_NOT_USABLE: + assert(channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL); + assert(!can_send_channel_update(channel)); + assert(!cg->refresh_timer); + return; + case CGOSSIP_NOT_DEEP_ENOUGH: + assert(channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL); + assert(can_send_channel_update(channel)); + assert(!cg->refresh_timer); + return; + case CGOSSIP_NEED_PEER_SIGS: + assert(can_send_channel_update(channel)); + assert(channel->scid); + /* If we have sigs, they don't match */ + if (cg->remote_sigs) + assert(!channel->scid || !short_channel_id_eq(&cg->remote_sigs->scid, channel->scid)); + assert(!cg->refresh_timer); + return; + case CGOSSIP_ANNOUNCED: + assert(can_send_channel_update(channel)); + assert(channel->scid); + assert(cg->remote_sigs); + return; + } + fatal("Bad channel_gossip_state %u", cg->state); +} + +/* Recursion */ +static void cupdate_timer_refresh(struct channel *channel); + +static void set_public_cupdate(struct channel *channel, + const u8 *cupdate TAKES) +{ + struct lightningd *ld = channel->peer->ld; + struct channel_gossip *cg = channel->channel_gossip; + u32 timestamp; + bool enabled; + struct timeabs now, due; + + if (!channel_update_details(cupdate, ×tamp, &enabled)) { + log_broken(channel->log, "Invalid channel_update %s: ignoring", + tal_hex(tmpctx, cupdate)); + if (taken(cupdate)) + tal_free(cupdate); + return; + } + + tal_free(cg->cupdate); + cg->cupdate = tal_dup_talarr(cg, u8, cupdate); + + cg->refresh_timer = tal_free(cg->refresh_timer); + + /* If enabled, we refresh, based on old timestamp */ + if (!enabled) + return; + + due.ts.tv_sec = timestamp; + due.ts.tv_nsec = 0; + due = timeabs_add(due, + time_from_sec(GOSSIP_PRUNE_INTERVAL(ld->dev_fast_gossip_prune) + - GOSSIP_BEFORE_DEADLINE(ld->dev_fast_gossip_prune))); + + /* In case it's passed, timer should be zero */ + now = time_now(); + if (time_after(now, due)) + due = now; + + cg->refresh_timer = new_reltimer(ld->timers, cg, + time_between(due, now), + cupdate_timer_refresh, + channel); +} + +static void msg_to_peer(const struct channel *channel, const u8 *msg TAKES) +{ + struct peer *peer = channel->peer; + struct lightningd *ld = peer->ld; + + /* Shutting down, or peer not connected? */ + if (ld->connectd && peer->connected == PEER_CONNECTED) { + subd_send_msg(ld->connectd, + take(towire_connectd_peer_send_msg(NULL, + &peer->id, + peer->connectd_counter, + msg))); + } + + if (taken(msg)) + tal_free(msg); +} + +static enum channel_gossip_state init_public_state(struct channel *channel, + const struct remote_announce_sigs *remote_sigs) +{ + struct lightningd *ld = channel->peer->ld; + + if (!can_send_channel_update(channel)) + return CGOSSIP_NOT_USABLE; + + /* Note: depth when we startup is not actually reliable, since + * we step one block back. We'll fix this up when gossipd + * tells us it's announced, or, when we add the block. */ + if (!channel_announceable(channel, get_block_height(ld->topology))) + return CGOSSIP_NOT_DEEP_ENOUGH; + + if (!remote_sigs) { + return CGOSSIP_NEED_PEER_SIGS; + } + + return CGOSSIP_ANNOUNCED; +} + +static u8 *sign_update(const tal_t *ctx, + struct lightningd *ld, + const u8 *unsigned_cupdate) +{ + const u8 *msg; + u8 *signed_update; + + /* Sign it please! */ + msg = hsm_sync_req(tmpctx, ld, + take(towire_hsmd_cupdate_sig_req(NULL, unsigned_cupdate))); + + if (!fromwire_hsmd_cupdate_sig_reply(ctx, msg, &signed_update)) + fatal("Reading cupdate_sig_reply: %s", tal_hex(tmpctx, msg)); + return signed_update; +} + +/* Try to send a channel_update direct to peer, unless redundant */ +static void send_private_cupdate(struct channel *channel, bool even_if_redundant) +{ + struct channel_gossip *cg = channel->channel_gossip; + const u8 *cupdate; + const struct short_channel_id *scid; + + /* BOLT #7: + * + * - 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` + */ + /* We prefer their alias, if possible: they might not have seen the block which + * mined the funding tx yet, so the scid would be meaningless to them. */ + if (channel->alias[REMOTE]) + scid = channel->alias[REMOTE]; + else + scid = channel->scid; + + /* Only useful channels: not if closing */ + if (!channel_state_can_add_htlc(channel->state)) + return; + + /* We always set "enabled" on unannounced channels, since if peer + * receives it, that's what it means */ + cupdate = unsigned_channel_update(tmpctx, channel, scid, + NULL, false, true); + + /* Suppress redundant ones (except on reconnect, in case it's + * lost) */ + if (cg->cupdate) { + if (!even_if_redundant + && channel_update_same(cg->cupdate, cupdate)) { + return; + } + tal_free(cg->cupdate); + } + + cg->cupdate = sign_update(cg, channel->peer->ld, cupdate); + msg_to_peer(channel, cg->cupdate); +} + +static void broadcast_public_cupdate_addgossip_reply(struct subd *gossip UNUSED, + const u8 *reply, + const int *fds UNUSED, + struct channel *channel) +{ + char *err; + + if (!fromwire_gossipd_addgossip_reply(reply, reply, &err)) + fatal("Reading broadcast_public_cupdate_addgossip_reply: %s", + tal_hex(tmpctx, reply)); + + if (strlen(err)) + log_broken(channel->log, "gossipd rejected our channel update: %s", err); +} + +/* Send gossipd a channel_update, if not redundant. */ +static void broadcast_public_cupdate(struct channel *channel, + bool ok_if_disconnected) +{ + struct lightningd *ld = channel->peer->ld; + struct channel_gossip *cg = channel->channel_gossip; + const u8 *cupdate; + u32 old_timestamp; + bool enable, have_old; + + /* If we have no previous channel_update, this fails */ + have_old = channel_update_details(cg->cupdate, + &old_timestamp, &enable); + + if (!channel_state_can_add_htlc(channel->state)) { + /* If it's (no longer) usable, it's a simply courtesy + * to disable */ + enable = false; + } else if (channel->owner) { + /* If it's live, it's enabled */ + enable = true; + } else if (starting_up) { + /* If we are starting up, don't change it! */ + if (!have_old) + /* Assume the best if we don't have an updated */ + enable = true; + } else { + enable = ok_if_disconnected; + } + + cupdate = unsigned_channel_update(tmpctx, channel, channel->scid, + have_old ? &old_timestamp : NULL, + true, + enable); + + /* Suppress redundant ones */ + if (cg->cupdate && channel_update_same(cg->cupdate, cupdate)) + return; + + set_public_cupdate(channel, + take(sign_update(NULL, channel->peer->ld, cupdate))); + + subd_req(ld->gossip, ld->gossip, + take(towire_gossipd_addgossip(NULL, cg->cupdate)), + -1, 0, broadcast_public_cupdate_addgossip_reply, channel); +} + +static void cupdate_timer_refresh(struct channel *channel) +{ + struct channel_gossip *cg = channel->channel_gossip; + + /* Don't try to free this again if set_public_cupdate called later */ + cg->refresh_timer = NULL; + + log_debug(channel->log, "Sending keepalive channel_update for %s", + short_channel_id_to_str(tmpctx, channel->scid)); + + /* Free old cupdate to force a new one to be generated */ + cg->cupdate = tal_free(cg->cupdate); + broadcast_public_cupdate(channel, true); +} + +static void stash_remote_announce_sigs(struct channel *channel, + struct short_channel_id scid, + const secp256k1_ecdsa_signature *node_sig, + const secp256k1_ecdsa_signature *bitcoin_sig) +{ + struct channel_gossip *cg = channel->channel_gossip; + + tal_free(cg->remote_sigs); + cg->remote_sigs = tal(cg, struct remote_announce_sigs); + cg->remote_sigs->scid = scid; + cg->remote_sigs->node_sig = *node_sig; + cg->remote_sigs->bitcoin_sig = *bitcoin_sig; + log_debug(channel->log, + "channel_gossip: received announcement sigs for %s (we have %s)", + short_channel_id_to_str(tmpctx, &scid), + channel->scid ? short_channel_id_to_str(tmpctx, channel->scid) : "none"); +} + +static bool apply_remote_sigs(struct channel *channel) +{ + struct channel_gossip *cg = channel->channel_gossip; + + if (!cg->remote_sigs) + return false; + + if (!short_channel_id_eq(&cg->remote_sigs->scid, channel->scid)) { + log_debug(channel->log, "We have remote sigs, but wrong scid!"); + return false; + } + + /* FIXME: check sigs are valid! */ + wallet_announcement_save(channel->peer->ld->wallet, + channel->dbid, + &cg->remote_sigs->node_sig, + &cg->remote_sigs->bitcoin_sig); + return true; +} + +static void send_channel_announce_sigs(struct channel *channel) +{ + /* First 2 + 256 byte are the signatures and msg type, skip them */ + const size_t offset = 258; + struct lightningd *ld = channel->peer->ld; + struct sha256_double hash; + secp256k1_ecdsa_signature local_node_sig, local_bitcoin_sig; + struct pubkey mykey; + const u8 *ca, *msg; + + /* If it's already closing, don't bother. */ + if (!channel_state_can_add_htlc(channel->state)) + return; + + ca = create_channel_announcement(tmpctx, channel, + NULL, NULL, NULL, NULL); + + msg = hsm_sync_req(tmpctx, ld, + take(towire_hsmd_sign_any_cannouncement_req(NULL, + ca, + &channel->peer->id, + channel->dbid))); + if (!fromwire_hsmd_sign_any_cannouncement_reply(msg, &local_node_sig, &local_bitcoin_sig)) + fatal("Reading hsmd_sign_any_cannouncement_reply: %s", tal_hex(tmpctx, msg)); + + /* Double-check that HSM gave valid signatures. */ + sha256_double(&hash, ca + offset, tal_count(ca) - offset); + if (!pubkey_from_node_id(&mykey, &ld->id)) + fatal("Could not convert own public key"); + + if (!check_signed_hash(&hash, &local_node_sig, &mykey)) { + channel_internal_error(channel, + "HSM returned an invalid node signature"); + return; + } + + if (!check_signed_hash(&hash, &local_bitcoin_sig, &channel->local_funding_pubkey)) { + channel_internal_error(channel, + "HSM returned an invalid bitcoin signature"); + return; + } + + msg = towire_announcement_signatures(NULL, + &channel->cid, channel->scid, + &local_node_sig, &local_bitcoin_sig); + msg_to_peer(channel, take(msg)); +} + +static void send_channel_announce_addgossip_reply(struct subd *gossip UNUSED, + const u8 *reply, + const int *fds UNUSED, + struct channel *channel) +{ + char *err; + + if (!fromwire_gossipd_addgossip_reply(reply, reply, &err)) + fatal("Reading send_channel_announce_addgossip_reply: %s", + tal_hex(tmpctx, reply)); + + if (strlen(err)) + log_broken(channel->log, "gossipd rejected our channel announcement: %s", err); +} + +static void send_channel_announcement(struct channel *channel) +{ + secp256k1_ecdsa_signature local_node_sig, local_bitcoin_sig; + struct lightningd *ld = channel->peer->ld; + const u8 *ca, *msg; + struct channel_gossip *cg = channel->channel_gossip; + + ca = create_channel_announcement(tmpctx, channel, + NULL, NULL, + &cg->remote_sigs->node_sig, + &cg->remote_sigs->bitcoin_sig); + + msg = hsm_sync_req(tmpctx, ld, + take(towire_hsmd_sign_any_cannouncement_req(NULL, ca, + &channel->peer->id, + channel->dbid))); + if (!fromwire_hsmd_sign_any_cannouncement_reply(msg, + &local_node_sig, + &local_bitcoin_sig)) + fatal("Reading hsmd_sign_any_cannouncement_reply: %s", tal_hex(tmpctx, msg)); + + /* Don't crash if shutting down */ + if (!ld->gossip) + return; + + ca = create_channel_announcement(tmpctx, channel, + &local_node_sig, + &local_bitcoin_sig, + &cg->remote_sigs->node_sig, + &cg->remote_sigs->bitcoin_sig); + + subd_req(ld->gossip, ld->gossip, + take(towire_gossipd_addgossip(NULL, ca)), + -1, 0, send_channel_announce_addgossip_reply, channel); + /* We can also send our first public channel_update now */ + broadcast_public_cupdate(channel, true); +} + +static void set_gossip_state(struct channel *channel, + enum channel_gossip_state state) +{ + struct channel_gossip *cg = channel->channel_gossip; + + cg->state = state; + + switch (cg->state) { + case CGOSSIP_PRIVATE: + abort(); + case CGOSSIP_NOT_USABLE: + return; + case CGOSSIP_NOT_DEEP_ENOUGH: + /* But it exists, so try sending private channel_update */ + send_private_cupdate(channel, false); + return; + case CGOSSIP_NEED_PEER_SIGS: + send_channel_announce_sigs(channel); + /* We may already have remote signatures */ + if (!apply_remote_sigs(channel)) + return; + cg->state = CGOSSIP_ANNOUNCED; + /* fall thru */ + case CGOSSIP_ANNOUNCED: + /* Any previous update was private, so clear. */ + cg->cupdate = tal_free(cg->cupdate); + send_channel_announcement(channel); + return; + } + fatal("Bad channel_gossip_state %u", cg->state); +} + +/* Initialize channel->channel_gossip state */ +void channel_gossip_init(struct channel *channel, + const struct peer_update *remote_update) +{ + struct lightningd *ld = channel->peer->ld; + struct channel_gossip *cg; + bool public = (channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL); + + cg = channel->channel_gossip = tal(channel, struct channel_gossip); + cg->cupdate = NULL; + cg->refresh_timer = NULL; + cg->peer_update = tal_dup_or_null(channel, struct peer_update, remote_update); + cg->remote_sigs = NULL; + + /* If we have an scid, we might have announcement signatures + * saved in the db already. */ + if (channel->scid && public) { + cg->remote_sigs = tal(cg, struct remote_announce_sigs); + cg->remote_sigs->scid = *channel->scid; + if (!wallet_remote_ann_sigs_load(ld->wallet, + channel, + &cg->remote_sigs->node_sig, + &cg->remote_sigs->bitcoin_sig)) { + cg->remote_sigs = tal_free(cg->remote_sigs); + } + } + + if (public) + cg->state = init_public_state(channel, cg->remote_sigs); + else + cg->state = CGOSSIP_PRIVATE; + + check_channel_gossip(channel); +} + +/* Something about channel changed: update if required */ +void channel_gossip_update(struct channel *channel) +{ + struct lightningd *ld = channel->peer->ld; + struct channel_gossip *cg = channel->channel_gossip; + + /* Ignore unsaved channels */ + if (!cg) + return; + + switch (cg->state) { + case CGOSSIP_NOT_USABLE: + /* Change might make it usable */ + if (!can_send_channel_update(channel)) { + check_channel_gossip(channel); + return; + } + set_gossip_state(channel, CGOSSIP_NOT_DEEP_ENOUGH); + /* fall thru */ + case CGOSSIP_NOT_DEEP_ENOUGH: + /* Now we can send at non-forwardable update */ + send_private_cupdate(channel, false); + /* Might have gotten straight from not-usable to announceable + * if we have a flurry of blocks, or minconf >= 6. */ + if (!channel_announceable(channel, get_block_height(ld->topology))) { + check_channel_gossip(channel); + return; + } + set_gossip_state(channel, CGOSSIP_NEED_PEER_SIGS); + /* Could have actually already had sigs! */ + if (cg->state == CGOSSIP_ANNOUNCED) + goto announced; + /* fall thru */ + case CGOSSIP_PRIVATE: + case CGOSSIP_NEED_PEER_SIGS: + send_private_cupdate(channel, false); + check_channel_gossip(channel); + return; + case CGOSSIP_ANNOUNCED: + announced: + /* We don't penalize disconnected clients normally: we only + * do that if we actually try to send an htlc through */ + broadcast_public_cupdate(channel, true); + check_channel_gossip(channel); + return; + } + fatal("Bad channel_gossip_state %u", channel->channel_gossip->state); +} + +void channel_gossip_got_announcement_sigs(struct channel *channel, + struct short_channel_id scid, + const secp256k1_ecdsa_signature *node_sig, + const secp256k1_ecdsa_signature *bitcoin_sig) +{ + /* Ignore unsaved channels */ + if (!channel->channel_gossip) { + log_broken(channel->log, "They sent an announcement_signatures message for a unsaved channel? Ignoring."); + return; + } + + switch (channel->channel_gossip->state) { + case CGOSSIP_PRIVATE: + log_unusual(channel->log, "They sent an announcement_signatures message for a private channel? Ignoring."); + u8 *warning = towire_warningfmt(NULL, + &channel->cid, + "You sent announcement_signatures for private channel"); + msg_to_peer(channel, take(warning)); + return; + case CGOSSIP_NOT_USABLE: + case CGOSSIP_NOT_DEEP_ENOUGH: + /* They're early? */ + stash_remote_announce_sigs(channel, + scid, node_sig, bitcoin_sig); + check_channel_gossip(channel); + return; + case CGOSSIP_NEED_PEER_SIGS: + stash_remote_announce_sigs(channel, + scid, node_sig, bitcoin_sig); + if (apply_remote_sigs(channel)) + set_gossip_state(channel, CGOSSIP_ANNOUNCED); + check_channel_gossip(channel); + return; + case CGOSSIP_ANNOUNCED: + /* BOLT #7: + * - upon reconnection (once the above timing requirements + * have been met): + * - MUST respond to the first `announcement_signatures` + * message with its own `announcement_signatures` message. + */ + send_channel_announce_sigs(channel); + check_channel_gossip(channel); + return; + } + fatal("Bad channel_gossip_state %u", channel->channel_gossip->state); +} + +/* Short channel id changed (splice, or reorg). */ +void channel_gossip_scid_changed(struct channel *channel) +{ + struct lightningd *ld = channel->peer->ld; + struct channel_gossip *cg = channel->channel_gossip; + + /* Ignore unsaved channels */ + if (!cg) + return; + + /* Clear any cached update, we'll need a new one! */ + cg->cupdate = tal_free(cg->cupdate); + + /* Any announcement signatures we received for old scid are no longer + * valid. */ + wallet_remote_ann_sigs_clear(ld->wallet, channel); + + switch (cg->state) { + case CGOSSIP_PRIVATE: + /* Still private, just send new channel_update */ + send_private_cupdate(channel, false); + check_channel_gossip(channel); + return; + case CGOSSIP_NOT_USABLE: + /* Shouldn't happen. */ + return; + case CGOSSIP_NOT_DEEP_ENOUGH: + case CGOSSIP_NEED_PEER_SIGS: + case CGOSSIP_ANNOUNCED: + log_debug(channel->log, "channel_gossip: scid now %s", + short_channel_id_to_str(tmpctx, channel->scid)); + /* Start again. */ + /* Maybe remote announcement signatures now apply? If not, + * free them */ + if (cg->remote_sigs + && !short_channel_id_eq(&cg->remote_sigs->scid, + channel->scid)) { + cg->remote_sigs = tal_free(cg->remote_sigs); + } + + /* Stop refresh timer, we're not announcing the old one. */ + cg->refresh_timer = tal_free(cg->refresh_timer); + + set_gossip_state(channel, + init_public_state(channel, cg->remote_sigs)); + send_channel_announce_sigs(channel); + check_channel_gossip(channel); + return; + } + fatal("Bad channel_gossip_state %u", cg->state); +} + +/* Block height changed */ +static void new_blockheight(struct lightningd *ld, + struct channel *channel, + u32 block_height) +{ + switch (channel->channel_gossip->state) { + case CGOSSIP_PRIVATE: + case CGOSSIP_NEED_PEER_SIGS: + case CGOSSIP_ANNOUNCED: + case CGOSSIP_NOT_USABLE: + return; + case CGOSSIP_NOT_DEEP_ENOUGH: + if (!channel_announceable(channel, block_height)) { + check_channel_gossip(channel); + return; + } + set_gossip_state(channel, CGOSSIP_NEED_PEER_SIGS); + check_channel_gossip(channel); + return; + } + fatal("Bad channel_gossip_state %u", channel->channel_gossip->state); +} + +void channel_gossip_notify_new_block(struct lightningd *ld, + u32 block_height) +{ + struct peer *peer; + struct channel *channel; + struct peer_node_id_map_iter it; + + for (peer = peer_node_id_map_first(ld->peers, &it); + peer; + peer = peer_node_id_map_next(ld->peers, &it)) { + list_for_each(&peer->channels, channel, list) { + /* Ignore unsaved channels */ + if (!channel->channel_gossip) + continue; + + new_blockheight(ld, channel, block_height); + check_channel_gossip(channel); + } + } +} + +/* Gossipd told us about a channel update on one of our channels (on loading) */ +void channel_gossip_update_from_gossipd(struct channel *channel, + const u8 *channel_update TAKES) +{ + if (!channel->channel_gossip) { + log_broken(channel->log, + "gossipd gave channel_update for unsaved channel? update=%s", + tal_hex(tmpctx, channel_update)); + return; + } + + /* If we didn't think it was announced already, it is now! */ + switch (channel->channel_gossip->state) { + case CGOSSIP_PRIVATE: + log_broken(channel->log, + "gossipd gave channel_update for private channel? update=%s", + tal_hex(tmpctx, channel_update)); + return; + case CGOSSIP_NOT_USABLE: + case CGOSSIP_NOT_DEEP_ENOUGH: + case CGOSSIP_NEED_PEER_SIGS: + set_gossip_state(channel, CGOSSIP_ANNOUNCED); + break; + case CGOSSIP_ANNOUNCED: + break; + } + + set_public_cupdate(channel, channel_update); + check_channel_gossip(channel); +} + +static void set_not_starting_up(struct lightningd *ld) +{ + starting_up = false; + log_debug(ld->log, "channel_gossip: no longer in startup mode"); +} + +/* Gossipd init is done: if you expected a channel_update, be + * disappointed. */ +void channel_gossip_init_done(struct lightningd *ld) +{ + struct peer *peer; + struct channel *channel; + struct peer_node_id_map_iter it; + + for (peer = peer_node_id_map_first(ld->peers, &it); + peer; + peer = peer_node_id_map_next(ld->peers, &it)) { + list_for_each(&peer->channels, channel, list) { + /* Ignore unsaved channels */ + if (!channel->channel_gossip) + continue; + + check_channel_gossip(channel); + if (channel->channel_gossip->cupdate) + continue; + if (channel->channel_gossip->state != CGOSSIP_ANNOUNCED) + continue; + /* gossipd lost announcement: re-create */ + log_unusual(channel->log, + "gossipd lost track of announced channel: re-announcing!"); + check_channel_gossip(channel); + send_channel_announcement(channel); + } + } + + notleak(new_reltimer(ld->timers, ld, + time_from_sec(60), + set_not_starting_up, ld)); +} + +/* Peer has connected. */ +/* FIXME: We really want "peer has reestablished */ +void channel_gossip_peer_connected(struct peer *peer) +{ + struct channel *channel; + + list_for_each(&peer->channels, channel, list) { + /* Ignore unsaved channels */ + if (!channel->channel_gossip) + continue; + + switch (channel->channel_gossip->state) { + case CGOSSIP_NOT_USABLE: + continue; + case CGOSSIP_PRIVATE: + case CGOSSIP_NOT_DEEP_ENOUGH: + send_private_cupdate(channel, true); + check_channel_gossip(channel); + continue; + case CGOSSIP_NEED_PEER_SIGS: + /* BOLT #7: + * - upon reconnection (once the above timing + * requirements have been met): + * ... + * - if it has NOT received an + * `announcement_signatures` message: + * - SHOULD retransmit the + * `announcement_signatures` message. + */ + send_private_cupdate(channel, true); + send_channel_announce_sigs(channel); + check_channel_gossip(channel); + continue; + case CGOSSIP_ANNOUNCED: + check_channel_gossip(channel); + continue; + } + fatal("Bad channel_gossip_state %u", channel->channel_gossip->state); + } +} + +/* We *could* send channel_updates for private channels, or + * unannounced. We do not */ +const u8 *channel_gossip_update_for_error(const tal_t *ctx, + struct channel *channel) +{ + /* We cannot ask this about unsaved channels. */ + struct channel_gossip *cg = channel->channel_gossip; + + switch (cg->state) { + case CGOSSIP_PRIVATE: + case CGOSSIP_NOT_USABLE: + case CGOSSIP_NOT_DEEP_ENOUGH: + case CGOSSIP_NEED_PEER_SIGS: + return NULL; + case CGOSSIP_ANNOUNCED: + broadcast_public_cupdate(channel, false); + check_channel_gossip(channel); + return cg->cupdate; + } + fatal("Bad channel_gossip_state %u", cg->state); +} + +/* BOLT #7: + * - MUST set the `short_channel_id` to either an `alias` it has + * received from the peer, or the real channel `short_channel_id`. + */ +/* But we used to get this wrong! Sot this is the only place where we + * look up by *remote* id. It's not unique, but it is unique for a + * specific peer. */ +static struct channel *lookup_by_peer_remote_alias(struct lightningd *ld, + const struct node_id *source, + struct short_channel_id scid) +{ + const struct peer *p; + struct channel *chan; + + if (!source) + return NULL; + + p = peer_by_id(ld, source); + if (!p) + return NULL; + + list_for_each(&p->channels, chan, list) { + if (chan->alias[REMOTE] + && short_channel_id_eq(&scid, chan->alias[REMOTE])) { + return chan; + } + } + return NULL; +} + +/* A peer sent gossipd an update_channel message for one of our channels. + * Gossipd checked the signature. + */ +void channel_gossip_set_remote_update(struct lightningd *ld, + const struct peer_update *update TAKES, + const struct node_id *source) +{ + struct channel *channel; + struct channel_gossip *cg; + + channel = any_channel_by_scid(ld, &update->scid, true); + if (!channel) { + channel = lookup_by_peer_remote_alias(ld, source, update->scid); + if (channel) + log_debug(channel->log, + "Bad gossip order: peer sent update using their own alias!"); + } + if (!channel) { + log_unusual(ld->log, "Bad gossip order: could not find channel %s for peer's " + "channel update", + short_channel_id_to_str(tmpctx, &update->scid)); + return; + } + + cg = channel->channel_gossip; + + if (!cg) { + log_broken(ld->log, "Peer sent update_channel for unsaved channel"); + return; + } + + /* For public channels, it could come from anywhere. Private + * channels must come from gossipd itself (the old store + * migration!) or the correct peer. */ + if (cg->state == CGOSSIP_PRIVATE + && source + && !node_id_eq(source, &channel->peer->id)) { + log_unusual(ld->log, "Bad gossip order: %s sent us a channel update for a " + "channel owned by %s (%s)", + type_to_string(tmpctx, struct node_id, source), + type_to_string(tmpctx, struct node_id, + &channel->peer->id), + type_to_string(tmpctx, struct short_channel_id, &update->scid)); + return; + } + + log_debug(ld->log, "updating channel %s with inbound settings", + type_to_string(tmpctx, struct short_channel_id, &update->scid)); + tal_free(cg->peer_update); + cg->peer_update = tal_dup(cg, struct peer_update, update); + wallet_channel_save(ld->wallet, channel); +} + +const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel) +{ + struct channel_gossip *cg = channel->channel_gossip; + + if (!cg) + return NULL; + return cg->peer_update; +} diff --git a/lightningd/channel_gossip.h b/lightningd/channel_gossip.h new file mode 100644 index 000000000000..9d49df911d5c --- /dev/null +++ b/lightningd/channel_gossip.h @@ -0,0 +1,55 @@ +#ifndef LIGHTNING_LIGHTNINGD_CHANNEL_GOSSIP_H +#define LIGHTNING_LIGHTNINGD_CHANNEL_GOSSIP_H +#include "config.h" +#include +#include + +struct channel; +struct lightningd; +struct peer; +struct peer_update; + +/* Initialize channel->channel_gossip state */ +void channel_gossip_init(struct channel *channel, + const struct peer_update *remote_update TAKES); + +/* Something about channel/blockchain changed: update if required */ +void channel_gossip_update(struct channel *channel); + +/* Short channel id changed (splice, or reorg). */ +void channel_gossip_scid_changed(struct channel *channel); + +/* Block height changed */ +void channel_gossip_notify_new_block(struct lightningd *ld, + u32 block_height); + +/* Got announcement_signatures from peer */ +void channel_gossip_got_announcement_sigs(struct channel *channel, + struct short_channel_id scid, + const secp256k1_ecdsa_signature *node_sig, + const secp256k1_ecdsa_signature *bitcoin_sig); + +/* Gossipd told us about a channel update on one of our channels (on loading) */ +void channel_gossip_update_from_gossipd(struct channel *channel, + const u8 *channel_update TAKES); + +/* Gossipd init is done: if you expected a channel_update, be + * disappointed. */ +void channel_gossip_init_done(struct lightningd *ld); + +/* Peer has connected. */ +void channel_gossip_peer_connected(struct peer *peer); + +/* Gossipd sent us this channel_update about the peer's side of the channle */ +void channel_gossip_set_remote_update(struct lightningd *ld, + const struct peer_update *update TAKES, + const struct node_id *source); + +/* Get channel_update to send in an error onion reply (can give NULL!) */ +const u8 *channel_gossip_update_for_error(const tal_t *ctx, + struct channel *channel); + +/* Get the peer's last-sent channel_update info, if any. */ +const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel); + +#endif /* LIGHTNING_LIGHTNINGD_CHANNEL_GOSSIP_H */ From 819def6dd791dc2d311a117a0bf9e1a2baf472e8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 13/38] lightningd: ignore gossip messages from channeld, switch to our own. This commit is a bit messy, but it tries to do the minimal switchover. Some tests change, so those are included here. Signed-off-by: Rusty Russell --- gossipd/routing.c | 17 +- lightningd/channel.c | 18 +- lightningd/channel.h | 8 +- lightningd/channel_control.c | 83 ++----- lightningd/channel_control.h | 3 - lightningd/closing_control.c | 7 - lightningd/dual_open_control.c | 24 +- lightningd/gossip_control.c | 230 ++------------------ lightningd/gossip_control.h | 12 - lightningd/lightningd.c | 2 + lightningd/opening_control.c | 3 + lightningd/pay.c | 4 +- lightningd/peer_control.c | 25 ++- lightningd/test/run-find_my_abspath.c | 4 + lightningd/test/run-invoice-select-inchan.c | 11 +- tests/test_connection.py | 54 ++--- tests/test_gossip.py | 25 +-- tests/test_misc.py | 6 +- tests/test_opening.py | 7 +- tests/test_pay.py | 4 + wallet/test/run-db.c | 6 + wallet/test/run-wallet.c | 55 ++++- wallet/wallet.c | 5 +- 23 files changed, 213 insertions(+), 400 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index 47b9aa8005fc..c1adad4bc142 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -587,9 +587,10 @@ static void bad_gossip_order(const u8 *msg, const char *details) { status_peer_debug(source_peer, - "Bad gossip order: %s before announcement %s", + "Bad gossip order: %s before announcement %s from %s", peer_wire_name(fromwire_peektype(msg)), - details); + details, + source_peer ? type_to_string(tmpctx, struct node_id, source_peer) : "local"); } struct chan *new_chan(struct routing_state *rstate, @@ -1791,6 +1792,18 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, owner = get_channel_owner(rstate, &short_channel_id, direction); if (!owner) { + /* This may be a local channel we don't know about. If it's from a peer, + * check signature assuming it's from that peer, and if it's valid, hand to ld */ + if (source_peer + && check_channel_update(tmpctx, source_peer, &signature, serialized) == NULL) { + tell_lightningd_peer_update(rstate, source_peer, + short_channel_id, fee_base_msat, + fee_proportional_millionths, + expiry, htlc_minimum, + htlc_maximum); + return NULL; + } + if (unknown_scid) *unknown_scid = short_channel_id; bad_gossip_order(serialized, diff --git a/lightningd/channel.c b/lightningd/channel.c index 204d49297469..7b8e0bc09fdc 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -10,8 +10,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -277,8 +279,6 @@ 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->peer_update = NULL; - channel->channel_update = NULL; channel->alias[REMOTE] = NULL; /* We don't even bother checking for clashes. */ channel->alias[LOCAL] = tal(channel, struct short_channel_id); @@ -327,6 +327,8 @@ struct channel *new_unsaved_channel(struct peer *peer, &channel->local_basepoints, &channel->local_funding_pubkey); + /* channel->channel_gossip gets populated once we know if it's public. */ + channel->channel_gossip = NULL; channel->forgets = tal_arr(channel, struct command *, 0); list_add_tail(&peer->channels, &channel->list); channel->rr_number = peer->ld->rr_counter++; @@ -572,9 +574,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->lease_commit_sig = tal_steal(channel, lease_commit_sig); channel->lease_chan_max_msat = lease_chan_max_msat; channel->lease_chan_max_ppt = lease_chan_max_ppt; - channel->peer_update = tal_steal(channel, peer_update); channel->blockheight_states = dup_height_states(channel, height_states); - channel->channel_update = NULL; /* DB migration, for example, sets min to 0, max to large: fixup */ htlc_min = channel->channel_info.their_config.htlc_minimum; @@ -598,6 +598,8 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->close_blockheight = NULL; channel->state_change_cause = reason; channel->ignore_fee_limits = ignore_fee_limits; + /* Populate channel->channel_gossip */ + channel_gossip_init(channel, take(peer_update)); /* Make sure we see any spends using this key */ if (!local_shutdown_scriptpubkey) { @@ -1098,3 +1100,11 @@ channel_scid_or_local_alias(const struct channel *chan) else return chan->alias[LOCAL]; } + +const u8 *channel_update_for_error(const tal_t *ctx, + struct channel *channel) +{ + /* FIXME: Call directly from callers */ + return channel_gossip_update_for_error(ctx, channel); +} + diff --git a/lightningd/channel.h b/lightningd/channel.h index 578a4f762bee..058bd4e4fde0 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -301,13 +301,6 @@ struct channel { /* Lease commited max part per thousandth channel fee (ppm * 1000) */ u16 lease_chan_max_ppt; - /* Channel incoming fee rates, cltv delta min/max htlc from - * peer. Used to generate route hints, blinded paths. */ - const struct peer_update *peer_update; - - /* 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; @@ -316,6 +309,7 @@ struct channel { bool ignore_fee_limits; }; +/* Is channel owned (and should be talking to peer) */ bool channel_is_connected(const struct channel *channel); /* For v2 opens, a channel that has not yet been committed/saved to disk */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index b2b04e3a2eb8..99bf5ba99211 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -682,6 +683,10 @@ bool depthcb_update_scid(struct channel *channel, return false; } + /* No change? Great. */ + if (channel->scid && short_channel_id_eq(channel->scid, &scid)) + return true; + if (!channel->scid) { wallet_annotate_txout(ld->wallet, outpoint, TX_CHANNEL_FUNDING, channel->dbid); @@ -696,17 +701,17 @@ bool depthcb_update_scid(struct channel *channel, if (channel->minimum_depth == 0) lockin_has_completed(channel, false); - wallet_channel_save(ld->wallet, channel); - } else if (!short_channel_id_eq(channel->scid, &scid)) { + } else { /* We freaked out if required when original was * removed, so just update now */ log_info(channel->log, "Short channel id changed from %s->%s", type_to_string(tmpctx, struct short_channel_id, channel->scid), type_to_string(tmpctx, struct short_channel_id, &scid)); *channel->scid = scid; - wallet_channel_save(ld->wallet, channel); + channel_gossip_scid_changed(channel); } + wallet_channel_save(ld->wallet, channel); return true; } @@ -886,12 +891,13 @@ void channel_record_open(struct channel *channel, u32 blockheight, bool record_p void lockin_has_completed(struct channel *channel, bool record_push) { + struct lightningd *ld = channel->peer->ld; + /* Fees might have changed (and we use IMMEDIATE once we're funded), * so update now. */ - try_update_feerates(channel->peer->ld, channel); + try_update_feerates(ld, channel); - try_update_blockheight(channel->peer->ld, channel, - get_block_height(channel->peer->ld->topology)); + try_update_blockheight(ld, channel, get_block_height(ld->topology)); /* Emit an event for the channel open (or channel proposal if blockheight * is zero) */ @@ -921,10 +927,6 @@ void lockin_complete(struct channel *channel, return; } - log_debug(channel->log, "Moving channel state from %s to %s", - channel_state_str(expected_state), - channel_state_str(CHANNELD_NORMAL)); - channel_set_state(channel, expected_state, CHANNELD_NORMAL, @@ -1057,10 +1059,10 @@ static void peer_got_announcement(struct channel *channel, const u8 *msg) { secp256k1_ecdsa_signature remote_ann_node_sig; secp256k1_ecdsa_signature remote_ann_bitcoin_sig; - struct short_channel_id unused; + struct short_channel_id scid; if (!fromwire_channeld_got_announcement(msg, - &unused, + &scid, &remote_ann_node_sig, &remote_ann_bitcoin_sig)) { channel_internal_error(channel, @@ -1069,9 +1071,9 @@ static void peer_got_announcement(struct channel *channel, const u8 *msg) return; } - wallet_announcement_save(channel->peer->ld->wallet, channel->dbid, - &remote_ann_node_sig, - &remote_ann_bitcoin_sig); + channel_gossip_got_announcement_sigs(channel, scid, + &remote_ann_node_sig, + &remote_ann_bitcoin_sig); } static void peer_got_shutdown(struct channel *channel, const u8 *msg) @@ -1234,23 +1236,6 @@ 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); @@ -1303,21 +1288,6 @@ static void handle_channel_upgrade(struct channel *channel, wallet_channel_save(channel->peer->ld->wallet, channel); } -static void handle_local_channel_update(struct channel *channel, - const u8 *msg) -{ - bool enable; - - if (!fromwire_channeld_local_channel_update(msg, &enable)) { - channel_internal_error(channel, - "bad channeld_local_channel_update %s", - tal_hex(channel, msg)); - return; - } - - tell_gossipd_local_channel_update(channel->peer->ld, channel, enable); -} - static void handle_local_anchors(struct channel *channel, const u8 *msg) { u64 remote_commitnum; @@ -1384,14 +1354,10 @@ 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; + /* FIXME: remove from channeld_wire.csv */ case WIRE_CHANNELD_LOCAL_CHANNEL_UPDATE: - handle_local_channel_update(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; case WIRE_CHANNELD_SPLICE_CONFIRMED_INIT: handle_splice_confirmed_init(sd->ld, sd->channel, msg); @@ -1476,8 +1442,7 @@ bool peer_start_channeld(struct channel *channel, const struct config *cfg = &ld->config; bool reached_announce_depth; struct secret last_remote_per_commit_secret; - secp256k1_ecdsa_signature *remote_ann_node_sig, *remote_ann_bitcoin_sig; - struct penalty_base *pbases; + secp256k1_ecdsa_signature *remote_ann_node_sig, *remote_ann_bitcoin_sig; struct penalty_base *pbases; u32 min_feerate, max_feerate, curr_blockheight; struct channel_inflight *inflight; struct inflight **inflights; @@ -1695,8 +1660,8 @@ bool peer_start_channeld(struct channel *channel, &last_remote_per_commit_secret, channel->peer->their_features, channel->remote_upfront_shutdown_script, - remote_ann_node_sig, - remote_ann_bitcoin_sig, + NULL, + NULL, channel->type, ld->dev_fast_gossip, ld->dev_disable_commit == -1 @@ -1977,12 +1942,6 @@ 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); -} - static struct command_result *param_channel_for_splice(struct command *cmd, const char *name, const char *buffer, diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index b0dbb075856b..638fac464f1f 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -42,9 +42,6 @@ void channel_record_open(struct channel *channel, u32 blockheight, bool record_p /* 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); - /* Tell channel about new feerates (owner must be channeld!) */ void channel_update_feerates(struct lightningd *ld, const struct channel *channel); diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index cd2745b7e8f6..7102cfc89308 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -508,13 +508,6 @@ void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd) /* We don't expect a response: it will give us feedback on * signatures sent and received, then closing_complete. */ subd_send_msg(channel->owner, take(initmsg)); - - /* Now tell gossipd that we're closing and that neither direction should - * be used. */ - if (channel->scid) - subd_send_msg(channel->peer->ld->gossip, - take(towire_gossipd_local_channel_close( - tmpctx, channel->scid))); } static struct command_result *param_outpoint(struct command *cmd, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 79bb9f91b4c1..29bd26503de4 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1632,24 +1633,6 @@ 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; @@ -3433,6 +3416,9 @@ static void handle_commit_ready(struct subd *dualopend, /* First time (not an RBF) */ if (channel->state == DUALOPEND_OPEN_INIT) { + /* Now we know if it's public or not, we can init channel_gossip */ + assert(channel->channel_gossip == NULL); + channel_gossip_init(channel, NULL); if (!(inflight = wallet_commit_channel(ld, channel, &funding, total_funding, @@ -3643,8 +3629,8 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND: channel_fail_fallen_behind(dualopend, msg); return 0; + /* FIXME: remove from dual_open_wire.csv */ case WIRE_DUALOPEND_LOCAL_PRIVATE_CHANNEL: - handle_local_private_channel(dualopend, msg); return 0; case WIRE_DUALOPEND_VALIDATE_INPUTS: handle_validate_inputs(dualopend, msg); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 6e2df87ffb20..e73bdc9d43f8 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -1,18 +1,24 @@ #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 @@ -121,140 +127,27 @@ static void handle_init_cupdate(struct lightningd *ld, const u8 *msg) tal_hex(msg, msg)); } - /* In theory this could vanish before gossipd gets around to telling - * us. */ channel = any_channel_by_scid(ld, &scid, true); if (!channel) { - log_unusual(ld->log, "init_cupdate for bad scid %s", - type_to_string(tmpctx, struct short_channel_id, - &scid)); + log_broken(ld->log, "init_cupdate for unknown scid %s", + type_to_string(tmpctx, struct short_channel_id, + &scid)); return; } - /* This should only happen on initialization, *but* gossipd also - * disabled channels on startup, so that can set this first. */ - if (!channel->channel_update) - channel->channel_update = tal_steal(channel, update); -} - -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, true); - if (!channel) { - log_unusual(ld->log, "Local update for bad scid %s", - type_to_string(tmpctx, struct short_channel_id, - &scid)); - return; - } - - channel_replace_update(channel, take(update)); -} - -const u8 *channel_update_for_error(const tal_t *ctx, - struct channel *channel) -{ - if (!channel->channel_update) - return NULL; - - /* Tell gossipd we're using it (if shutting down, might be NULL) */ - if (channel->peer->ld->gossip && channel->scid) { - subd_send_msg(channel->peer->ld->gossip, - take(towire_gossipd_used_local_channel_update - (NULL, channel->scid))); - } - - return tal_dup_talarr(ctx, u8, channel->channel_update); -} - -static void set_channel_remote_update(struct lightningd *ld, - struct channel *channel, - const struct node_id *source, - struct peer_update *update TAKES) -{ - struct short_channel_id *scid; - - scid = channel->scid; - if (!scid) - scid = channel->alias[LOCAL]; - - /* NULL source means it's from gossipd itself */ - if (source && !node_id_eq(source, &channel->peer->id)) { - log_unusual(ld->log, "Bad gossip order: %s sent us a channel update for a " - "channel owned by %s (%s)", - type_to_string(tmpctx, struct node_id, source), - type_to_string(tmpctx, struct node_id, - &channel->peer->id), - type_to_string(tmpctx, struct short_channel_id, scid)); - if (taken(update)) - tal_free(update); - return; - } - log_debug(ld->log, "updating channel %s with private inbound settings", - type_to_string(tmpctx, struct short_channel_id, scid)); - tal_free(channel->peer_update); - channel->peer_update = tal_dup(channel, struct peer_update, update); - if (taken(update)) - tal_free(update); - wallet_channel_save(ld->wallet, channel); -} - -/* One of the few places where we look up by *remote* id. It's not unique, - * but it is unique for a specific peer. */ -static struct channel *lookup_by_peer_remote_alias(struct lightningd *ld, - const struct node_id *source, - struct short_channel_id scid) -{ - const struct peer *p; - struct channel *chan; - - if (!source) - return NULL; - - p = peer_by_id(ld, source); - if (!p) - return NULL; - - list_for_each(&p->channels, chan, list) { - if (chan->alias[REMOTE] - && short_channel_id_eq(&scid, chan->alias[REMOTE])) - return chan; - } - return NULL; + channel_gossip_update_from_gossipd(channel, take(update)); } static void handle_peer_update_data(struct lightningd *ld, const u8 *msg) { - struct channel *channel; - struct peer_update *update; + struct peer_update update; struct node_id *source; - update = tal(tmpctx, struct peer_update); - if (!fromwire_gossipd_remote_channel_update(msg, msg, &source, update)) + if (!fromwire_gossipd_remote_channel_update(msg, msg, &source, &update)) fatal("Gossip gave bad GOSSIPD_REMOTE_CHANNEL_UPDATE %s", tal_hex(msg, msg)); - channel = any_channel_by_scid(ld, &update->scid, true); - if (!channel) - channel = lookup_by_peer_remote_alias(ld, source, update->scid); - if (!channel) { - log_unusual(ld->log, "Bad gossip order: could not find channel %s for peer's " - "channel update", - short_channel_id_to_str(tmpctx, &update->scid)); - return; - } - set_channel_remote_update(ld, channel, source, update); + channel_gossip_set_remote_update(ld, &update, source); } static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) @@ -275,9 +168,9 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: case WIRE_GOSSIPD_ADDGOSSIP: case WIRE_GOSSIPD_GET_ADDRS: + case WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT: 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: @@ -292,12 +185,12 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_INIT_CUPDATE: handle_init_cupdate(gossip->ld, msg); break; + /* FIXME: remove from gossipd_wire.csv */ + case WIRE_GOSSIPD_GOT_LOCAL_CHANNEL_UPDATE: + break; 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; case WIRE_GOSSIPD_REMOTE_CHANNEL_UPDATE: /* Please stash in database for us! */ handle_peer_update_data(gossip->ld, msg); @@ -347,6 +240,10 @@ static void gossipd_init_done(struct subd *gossipd, const int *fds, void *unused) { + /* Any channels without channel_updates, we populate now: gossipd + * might have lost its gossip_store. */ + channel_gossip_init_done(gossipd->ld); + /* Break out of loop, so we can begin */ log_debug(gossipd->ld->log, "io_break: %s", __func__); io_break(gossipd); @@ -405,91 +302,6 @@ void gossipd_notify_spends(struct lightningd *ld, scids))); } -/* Tell gossipd about latest channel_update. */ -void tell_gossipd_local_channel_update(struct lightningd *ld, - struct channel *channel, - bool enable) -{ - /* 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, - channel->scid ? channel->scid : channel->alias[LOCAL], - enable, - ld->config.cltv_expiry_delta, - channel->htlc_minimum_msat, - channel->feerate_base, - channel->feerate_ppm, - channel->htlc_maximum_msat, - channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL))); -} - -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) -{ - /* 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, - 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, const char *buffer, const jsmntok_t *obj UNNEEDED, diff --git a/lightningd/gossip_control.h b/lightningd/gossip_control.h index 6e5b37cbbeb2..cc9827b056a2 100644 --- a/lightningd/gossip_control.h +++ b/lightningd/gossip_control.h @@ -16,16 +16,4 @@ void gossipd_notify_spends(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, - bool enabled); -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 */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index d8120f4e2fd4..778bf26ed6bc 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -755,6 +756,7 @@ void notify_new_block(struct lightningd *ld, u32 block_height) /* Inform our subcomponents individually. */ htlcs_notify_new_block(ld, block_height); channel_notify_new_block(ld, block_height); + channel_gossip_notify_new_block(ld, block_height); gossip_notify_new_block(ld, block_height); waitblockheight_notify_new_block(ld, block_height); } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index c13efb4226d5..5410f6468ce5 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1506,6 +1506,9 @@ static struct channel *stub_chan(struct command *cmd, false, NULL); + /* We don't want to gossip about this, ever. */ + channel->channel_gossip = tal_free(channel->channel_gossip); + return channel; } diff --git a/lightningd/pay.c b/lightningd/pay.c index 1e759c7fdaa0..fa0dddc61d46 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -808,7 +808,7 @@ find_channel_for_htlc_add(struct lightningd *ld, const struct amount_msat *amount) { struct channel *channel; - struct short_channel_id *scid; + const struct short_channel_id *scid; struct peer *peer = peer_by_id(ld, node); if (!peer) return NULL; @@ -843,7 +843,7 @@ find_channel_for_htlc_add(struct lightningd *ld, return NULL; found: - scid = channel->scid ? channel->scid : channel->alias[LOCAL]; + scid = channel_scid_or_local_alias(channel); log_debug( ld->log, "Selected channel %s (%s) for selector %s (%s)", short_channel_id_to_str(tmpctx, scid), diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 21bdcac929d1..ee2a6a93a06d 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -816,6 +817,7 @@ static void json_add_channel(struct lightningd *ld, struct amount_msat funding_msat; struct amount_sat peer_funded_sats; struct state_change_entry *state_changes; + const struct peer_update *peer_update; u32 feerate; json_object_start(response, key); @@ -841,19 +843,21 @@ static void json_add_channel(struct lightningd *ld, json_add_u32(response, "fee_proportional_millionths", channel->feerate_ppm); json_object_end(response); - if (channel->peer_update) { + + peer_update = channel_gossip_get_remote_update(channel); + if (peer_update) { json_object_start(response, "remote"); json_add_amount_msat(response, "htlc_minimum_msat", - channel->peer_update->htlc_minimum_msat); + peer_update->htlc_minimum_msat); json_add_amount_msat(response, "htlc_maximum_msat", - channel->peer_update->htlc_maximum_msat); - json_add_u32(response, "cltv_expiry_delta", channel->peer_update->cltv_delta); + peer_update->htlc_maximum_msat); + json_add_u32(response, "cltv_expiry_delta", peer_update->cltv_delta); json_add_amount_msat(response, "fee_base_msat", - amount_msat(channel->peer_update->fee_base)); + amount_msat(peer_update->fee_base)); json_add_u32(response, "fee_proportional_millionths", - channel->peer_update->fee_ppm); + peer_update->fee_ppm); json_object_end(response); } json_object_end(response); @@ -1395,6 +1399,8 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa connect_activate_subd(ld, channel); } } + + channel_gossip_peer_connected(peer); return; send_error: @@ -2004,6 +2010,8 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, return DELETE_WATCH; case CHANNELD_AWAITING_LOCKIN: + /* This may be redundant, and may be public later, but + * make sure we tell gossipd at least once */ if (depth >= channel->minimum_depth && channel->remote_channel_ready) { lockin_complete(channel, CHANNELD_AWAITING_LOCKIN); @@ -2012,6 +2020,7 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, case CHANNELD_NORMAL: case CHANNELD_AWAITING_SPLICE: channeld_tell_depth(channel, txid, depth); + if (depth < ANNOUNCE_MIN_DEPTH || depth < channel->minimum_depth) return KEEP_WATCHING; /* Normal state and past announce depth? Stop bothering us! */ @@ -2920,10 +2929,6 @@ static void set_channel_config(struct command *cmd, struct channel *channel, channel_update_feerates(cmd->ld, channel); } - /* Tell gossipd */ - /* FIXME: this always enables channel, even if not enabled! */ - tell_gossipd_local_channel_update(cmd->ld, channel, true); - /* save values to database */ wallet_channel_save(cmd->ld->wallet, channel); diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index ba8475fff6f6..94b7e527dad1 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -10,6 +10,10 @@ int unused_main(int argc, char *argv[]); /* Generated stub for begin_topology */ void begin_topology(struct chain_topology *topo UNNEEDED) { fprintf(stderr, "begin_topology called!\n"); abort(); } +/* Generated stub for channel_gossip_notify_new_block */ +void channel_gossip_notify_new_block(struct lightningd *ld UNNEEDED, + u32 block_height UNNEEDED) +{ fprintf(stderr, "channel_gossip_notify_new_block called!\n"); abort(); } /* Generated stub for channel_notify_new_block */ void channel_notify_new_block(struct lightningd *ld UNNEEDED, u32 block_height UNNEEDED) diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 09991355aa7b..5e794c418b94 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -84,6 +84,12 @@ void channel_fail_transient(struct channel *channel UNNEEDED, bool disconnect UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "channel_fail_transient called!\n"); abort(); } +/* Generated stub for channel_gossip_get_remote_update */ +const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_gossip_get_remote_update called!\n"); abort(); } +/* Generated stub for channel_gossip_peer_connected */ +void channel_gossip_peer_connected(struct peer *peer UNNEEDED) +{ fprintf(stderr, "channel_gossip_peer_connected 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(); } @@ -913,11 +919,6 @@ void subd_send_fd(struct subd *sd UNNEEDED, int fd UNNEEDED) /* 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(); } -/* Generated stub for tell_gossipd_local_channel_update */ -void tell_gossipd_local_channel_update(struct lightningd *ld UNNEEDED, - struct channel *channel UNNEEDED, - bool enabled UNNEEDED) -{ fprintf(stderr, "tell_gossipd_local_channel_update 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/tests/test_connection.py b/tests/test_connection.py index 87414c066171..2646be97c960 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -971,37 +971,44 @@ def test_shutdown_reconnect(node_factory): assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "sqlite3-specific DB manip") 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}) - - # When l1 restarts (with rescan=1), make it think it hasn't - # reached announce_depth, so it wont re-send announcement_signatures - def no_blocks_above(req): - if req['params'][0] > 107: - return {"result": None, - "error": {"code": -8, "message": "Block height out of range"}, "id": req['id']} - else: - return {'result': l1.bitcoin.rpc.getblockhash(req['params'][0]), - "error": None, 'id': req['id']} + l1, l2 = node_factory.line_graph(2, wait_for_announce=True, opts={'may_reconnect': True, + 'dev-no-reconnect': None}) - l1.daemon.rpcproxy.mock_rpc('getblockhash', no_blocks_above) - l1.restart() + # Wipe l2's gossip_store + l2.stop() + gs_path = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, 'gossip_store') + os.unlink(gs_path) + l2.start() # l2 will now uses (REMOTE's) announcement_signatures it has stored - wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['status'] == [ - 'CHANNELD_NORMAL:Reconnected, and reestablished.', - 'CHANNELD_NORMAL:Channel ready for use. Channel announced.']) + wait_for(lambda: l2.rpc.listchannels()['channels'] != []) + + # Remove remote signatures from l1 so it asks for them (and delete gossip store) + l1.db_manip("UPDATE channels SET remote_ann_node_sig=NULL, remote_ann_bitcoin_sig=NULL") + gs_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store') + os.unlink(gs_path) + l1.restart() + + l1.connect(l2) + l1needle = l1.daemon.logsearch_start + l2needle = l2.daemon.logsearch_start + + # l1 asks once, l2 replies once. + # Make sure we get all the msgs! + time.sleep(5) - # But l2 still sends its own sigs on reconnect - l2.daemon.wait_for_logs([r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES', - r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES']) + l1.daemon.wait_for_log('peer_out WIRE_ANNOUNCEMENT_SIGNATURES') + l2.daemon.wait_for_log('peer_out WIRE_ANNOUNCEMENT_SIGNATURES') - count = ''.join(l1.daemon.logs).count(r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES') + l1msgs = [l.split()[4] for l in l1.daemon.logs[l1needle:] if 'WIRE_ANNOUNCEMENT_SIGNATURES' in l] + assert l1msgs == ['peer_out', 'peer_in'] - # l1 only did send them the first time - assert(count == 1 or count == 2) + # l2 only sends one. + assert len([l for l in l2.daemon.logs[l2needle:] if 'peer_out WIRE_ANNOUNCEMENT_SIGNATURES' in l]) == 1 @pytest.mark.openchannel('v1') @@ -3527,9 +3534,6 @@ def test_wumbo_channels(node_factory, bitcoind): # And we can wumbo pay, right? inv = l2.rpc.invoice(str(1 << 24) + "sat", "test_wumbo_channels", "wumbo payment") - # We actually do warn about capacity: l2 sees that *l1* doesn't have - # enough incoming to pay (not knowing that l1 is the intended payer). - assert 'warning_capacity' in inv assert 'warning_mpp' not in inv l1.rpc.pay(inv['bolt11']) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 52ae05f99c17..3851a3939429 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -15,7 +15,6 @@ import math import os import pytest -import re import struct import subprocess import time @@ -270,8 +269,6 @@ 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, opts={'log-level': 'io'}) genesis_blockhash = chainparams['chain_hash'] @@ -311,14 +308,14 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): # Now timestamp which doesn't overlap (gives nothing). msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, - '0', before_anything - backdate, + '0', before_anything, filters=['0109', '0107', '0012']) assert msgs == [] # Now choose range which will only give first update. msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, - before_anything - backdate, + before_anything, after_12 - before_anything + 1, filters=['0109', '0107', '0012']) @@ -332,7 +329,7 @@ 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, - before_23 - backdate, + before_23, after_23 - before_23 + 1, filters=['0109', '0107', '0012']) @@ -1627,7 +1624,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("0c" # GOSSIP_STORE_VERSION + f.write(bytearray.fromhex("0d" # GOSSIP_STORE_VERSION "000001b0" # len "fea676e8" # csum "5b8d9b44" # timestamp @@ -1654,7 +1651,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("0c") + assert bytearray(f.read()) == bytearray.fromhex("0d") def test_gossip_store_compact_on_load(node_factory, bitcoind): @@ -1668,15 +1665,9 @@ def test_gossip_store_compact_on_load(node_factory, bitcoind): l2.restart() # These appear before we're fully started, so will already in log: - # FIXME: this will change! - line = l2.daemon.is_in_log(r'gossip_store_compact_offline: .* deleted, .* copied') - m = re.search(r'gossip_store_compact_offline: (.*) deleted', line) - # We can have private re-tranmissions, but at minumum we had a deleted private - # channel message and two private updates, then two deleted updates. - assert int(m.group(1)) >= 5 - - # FIXME: this will change! - assert l2.daemon.is_in_log(r'gossip_store: Read 2/4/[23]/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in [0-9]* bytes') + l2.daemon.is_in_log(r'gossip_store_compact_offline: 2 deleted, 9 copied') + + assert l2.daemon.is_in_log(r'gossip_store: Read 2/4/3/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 2016 bytes') def test_gossip_announce_invalid_block(node_factory, bitcoind): diff --git a/tests/test_misc.py b/tests/test_misc.py index eda55d0464d5..09410cd6a146 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1311,7 +1311,8 @@ def no_more_blocks(req): bitcoind.generate_block(1) l1.daemon.wait_for_log(r'Short channel id changed from 103x1x0->104x1x0') - l2.daemon.wait_for_logs([r'Peer transient failure in CHANNELD_NORMAL: channeld sent Bad node_signature*']) + # We are OK with this! + l2.daemon.wait_for_log(r'channel_gossip: received announcement sigs for 104x1x0 \(we have 103x1x0\)') # Unblinding l2 brings it back in sync, restarts channeld and sends its announce sig l2.daemon.rpcproxy.mock_rpc('getblockhash', None) @@ -2794,7 +2795,8 @@ def test_restorefrompeer(node_factory, bitcoind): """ l1, l2 = node_factory.get_nodes(2, [{'allow_broken_log': True, 'experimental-peer-storage': None, - 'may_reconnect': True}, + 'may_reconnect': True, + 'allow_bad_gossip': True}, {'experimental-peer-storage': None, 'may_reconnect': True}]) diff --git a/tests/test_opening.py b/tests/test_opening.py index 78f38d304847..d0f9b83116de 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1724,10 +1724,9 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): l1.connect(l2) 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) - l1.daemon.wait_for_log(r'Got WIRE_HSMD_CUPDATE_SIG_REQ') - l2.daemon.wait_for_log(r'Got WIRE_HSMD_CUPDATE_SIG_REQ') + # Wait for the alias to be sent to peer. + wait_for(lambda: 'remote' in only_one(l1.rpc.listpeerchannels()['channels'])['updates']) + wait_for(lambda: 'remote' in only_one(l2.rpc.listpeerchannels()['channels'])['updates']) l1chan = only_one(l1.rpc.listpeerchannels()['channels']) l2chan = only_one(l2.rpc.listpeerchannels()['channels']) diff --git a/tests/test_pay.py b/tests/test_pay.py index 28bf7c5efb37..ad157ce442b5 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -310,6 +310,10 @@ def test_pay_get_error_with_update(node_factory): wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['peer_connected'] is False) assert(l1.is_channel_active(chanid2)) + + # Make sure it's not doing startup any more (where it doesn't disable channels!) + l1.daemon.wait_for_log("channel_gossip: no longer in startup mode", timeout=70) + with pytest.raises(RpcError, match=r'WIRE_TEMPORARY_CHANNEL_FAILURE'): l1.rpc.pay(inv['bolt11']) diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index bb6afec36ac5..6d23b9000c33 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -25,6 +25,12 @@ static void db_log_(struct logger *log UNUSED, enum log_level level UNUSED, cons /* Generated stub for bip32_pubkey */ void bip32_pubkey(struct lightningd *ld UNNEEDED, struct pubkey *pubkey UNNEEDED, u32 index UNNEEDED) { fprintf(stderr, "bip32_pubkey called!\n"); abort(); } +/* Generated stub for channel_gossip_get_remote_update */ +const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_gossip_get_remote_update called!\n"); abort(); } +/* Generated stub for channel_gossip_update */ +void channel_gossip_update(struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_gossip_update called!\n"); abort(); } /* Generated stub for channel_scid_or_local_alias */ const struct short_channel_id *channel_scid_or_local_alias(const struct channel *chan UNNEEDED) { fprintf(stderr, "channel_scid_or_local_alias called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index ebc7e4ce3af3..a3faf0285a8d 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -26,6 +26,7 @@ static void test_error(struct lightningd *ld, bool fatal, const char *fmt, va_li #include "lightningd/peer_control.c" #include "lightningd/peer_htlcs.c" #include "lightningd/channel.c" +#include "lightningd/channel_gossip.c" #include "lightningd/pay.c" #include "lightningd/wait.c" @@ -85,18 +86,22 @@ void broadcast_tx_(const tal_t *ctx UNNEEDED, /* Generated stub for channel_unsaved_close_conn */ void channel_unsaved_close_conn(struct channel *channel UNNEEDED, const char *why UNNEEDED) { fprintf(stderr, "channel_unsaved_close_conn called!\n"); abort(); } +/* Generated stub for channel_update_details */ +bool channel_update_details(const u8 *channel_update UNNEEDED, + u32 *timestamp UNNEEDED, + bool *enabled UNNEEDED) +{ fprintf(stderr, "channel_update_details called!\n"); abort(); } /* Generated stub for channel_update_feerates */ void channel_update_feerates(struct lightningd *ld UNNEEDED, const struct channel *channel UNNEEDED) { fprintf(stderr, "channel_update_feerates called!\n"); abort(); } -/* Generated stub for channel_update_for_error */ -const u8 *channel_update_for_error(const tal_t *ctx UNNEEDED, - struct channel *channel UNNEEDED) -{ fprintf(stderr, "channel_update_for_error called!\n"); abort(); } /* Generated stub for channel_update_reserve */ void channel_update_reserve(struct channel *channel UNNEEDED, struct channel_config *their_config UNNEEDED, struct amount_sat funding_total UNNEEDED) { fprintf(stderr, "channel_update_reserve called!\n"); abort(); } +/* Generated stub for channel_update_same */ +bool channel_update_same(const u8 *cupdate1 UNNEEDED, const u8 *cupdate2 UNNEEDED) +{ fprintf(stderr, "channel_update_same called!\n"); abort(); } /* Generated stub for channeld_tell_depth */ void channeld_tell_depth(struct channel *channel UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, @@ -166,6 +171,14 @@ struct anchor_details *create_anchor_details(const tal_t *ctx UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED) { fprintf(stderr, "create_anchor_details called!\n"); abort(); } +/* Generated stub for create_channel_announcement */ +u8 *create_channel_announcement(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const secp256k1_ecdsa_signature *local_node_signature UNNEEDED, + const secp256k1_ecdsa_signature *local_bitcoin_signature UNNEEDED, + const secp256k1_ecdsa_signature *remote_node_signature UNNEEDED, + const secp256k1_ecdsa_signature *remote_bitcoin_signature UNNEEDED) +{ fprintf(stderr, "create_channel_announcement called!\n"); abort(); } /* Generated stub for create_onionpacket */ struct onionpacket *create_onionpacket( const tal_t * ctx UNNEEDED, @@ -261,12 +274,18 @@ bool fromwire_connectd_peer_spoke(const tal_t *ctx UNNEEDED, const void *p UNNEE /* 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_gossipd_addgossip_reply */ +bool fromwire_gossipd_addgossip_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, wirestring **err UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_addgossip_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_check_pubkey_reply */ bool fromwire_hsmd_check_pubkey_reply(const void *p UNNEEDED, bool *ok UNNEEDED) { fprintf(stderr, "fromwire_hsmd_check_pubkey_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_client_hsmfd_reply */ bool fromwire_hsmd_client_hsmfd_reply(const void *p UNNEEDED) { fprintf(stderr, "fromwire_hsmd_client_hsmfd_reply 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(); } /* Generated stub for fromwire_hsmd_derive_secret_reply */ bool fromwire_hsmd_derive_secret_reply(const void *p UNNEEDED, struct secret *secret UNNEEDED) { fprintf(stderr, "fromwire_hsmd_derive_secret_reply called!\n"); abort(); } @@ -285,6 +304,9 @@ bool fromwire_hsmd_init_reply_v4(const tal_t *ctx UNNEEDED, const void *p UNNEED /* 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_any_cannouncement_reply */ +bool fromwire_hsmd_sign_any_cannouncement_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *node_signature UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_sign_any_cannouncement_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(); } @@ -954,11 +976,6 @@ void subkey_from_hmac(const char *prefix UNNEEDED, const struct secret *base UNNEEDED, struct secret *key UNNEEDED) { fprintf(stderr, "subkey_from_hmac called!\n"); abort(); } -/* Generated stub for tell_gossipd_local_channel_update */ -void tell_gossipd_local_channel_update(struct lightningd *ld UNNEEDED, - struct channel *channel UNNEEDED, - bool enabled UNNEEDED) -{ fprintf(stderr, "tell_gossipd_local_channel_update called!\n"); abort(); } /* Generated stub for to_canonical_invstr */ const char *to_canonical_invstr(const tal_t *ctx UNNEEDED, const char *invstring UNNEEDED) { fprintf(stderr, "to_canonical_invstr called!\n"); abort(); } @@ -969,6 +986,9 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "topology_add_sync_waiter_ called!\n"); abort(); } +/* Generated stub for towire_announcement_signatures */ +u8 *towire_announcement_signatures(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED, const secp256k1_ecdsa_signature *node_signature UNNEEDED, const secp256k1_ecdsa_signature *bitcoin_signature UNNEEDED) +{ fprintf(stderr, "towire_announcement_signatures 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(); } @@ -1025,6 +1045,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_addgossip */ +u8 *towire_gossipd_addgossip(const tal_t *ctx UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "towire_gossipd_addgossip 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(); } @@ -1034,6 +1057,9 @@ u8 *towire_hsmd_check_pubkey(const tal_t *ctx UNNEEDED, u32 index UNNEEDED, cons /* Generated stub for towire_hsmd_client_hsmfd */ u8 *towire_hsmd_client_hsmfd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 dbid UNNEEDED, u64 permissions UNNEEDED) { fprintf(stderr, "towire_hsmd_client_hsmfd 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(); } /* Generated stub for towire_hsmd_derive_secret */ u8 *towire_hsmd_derive_secret(const tal_t *ctx UNNEEDED, const u8 *info UNNEEDED) { fprintf(stderr, "towire_hsmd_derive_secret called!\n"); abort(); } @@ -1049,6 +1075,9 @@ u8 *towire_hsmd_init(const tal_t *ctx UNNEEDED, const struct bip32_key_version * /* 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_any_cannouncement_req */ +u8 *towire_hsmd_sign_any_cannouncement_req(const tal_t *ctx UNNEEDED, const u8 *ca UNNEEDED, const struct node_id *peerid UNNEEDED, u64 channel_dbid UNNEEDED) +{ fprintf(stderr, "towire_hsmd_sign_any_cannouncement_req 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, u64 commit_num UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_commitment_tx called!\n"); abort(); } @@ -1116,6 +1145,14 @@ void try_reconnect(const tal_t *ctx UNNEEDED, struct peer *peer UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "try_reconnect called!\n"); abort(); } +/* Generated stub for unsigned_channel_update */ +u8 *unsigned_channel_update(const tal_t *ctx UNNEEDED, + const struct channel *channel UNNEEDED, + const struct short_channel_id *scid UNNEEDED, + const u32 *old_timestamp UNNEEDED, + bool forwardable UNNEEDED, + bool enabled UNNEEDED) +{ fprintf(stderr, "unsigned_channel_update called!\n"); abort(); } /* Generated stub for unwrap_onionreply */ u8 *unwrap_onionreply(const tal_t *ctx UNNEEDED, const struct secret *shared_secrets UNNEEDED, diff --git a/wallet/wallet.c b/wallet/wallet.c index afeff42f29e8..0279e8180f6c 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -2357,7 +2358,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt); db_bind_int(stmt, chan->ignore_fee_limits); - peer_update = chan->peer_update; + peer_update = channel_gossip_get_remote_update(chan); if (peer_update) { db_bind_int(stmt, peer_update->fee_base); db_bind_int(stmt, peer_update->fee_ppm); @@ -2460,6 +2461,8 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_talarr(stmt, last_sent_commit); db_bind_u64(stmt, chan->dbid); db_exec_prepared_v2(take(stmt)); + + channel_gossip_update(chan); } void wallet_state_change_add(struct wallet *w, From 714bd1cfd661069a25fa728787c2de09aa5cd0be Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:17 +1030 Subject: [PATCH 14/38] channeld: remove channel_update message creation. We don't have to generate these any more: lightningd does it. Signed-off-by: Rusty Russell --- channeld/channeld.c | 469 +---------------------------------- channeld/channeld_wire.csv | 21 -- lightningd/channel_control.c | 34 +-- 3 files changed, 13 insertions(+), 511 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 0b9fe2bb46d1..ccf82a8ad77e 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -128,16 +127,8 @@ struct peer { /* Current blockheight */ u32 our_blockheight; - /* Announcement related information */ - struct node_id node_ids[NUM_SIDES]; + /* FIXME: Remove this. */ struct short_channel_id short_channel_ids[NUM_SIDES]; - secp256k1_ecdsa_signature announcement_node_sigs[NUM_SIDES]; - secp256k1_ecdsa_signature announcement_bitcoin_sigs[NUM_SIDES]; - bool have_sigs[NUM_SIDES]; - bool send_duplicate_announce_sigs; - - /* Which direction of the channel do we control? */ - u16 channel_direction; /* Local scid alias */ struct short_channel_id local_alias; @@ -175,18 +166,12 @@ struct peer { /* 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; - /* Information used for reestablishment. */ bool last_was_revoke; struct changed_htlc *last_sent_commit; u64 revocations_received; u8 channel_flags; - bool announce_depth_reached; - bool channel_local_active; - /* Make sure timestamps move forward. */ u32 last_update_timestamp; @@ -205,27 +190,16 @@ struct peer { /* We allow a 'tx-sigs' message between reconnect + channel_ready */ 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; - /* --experimental-upgrade-protocol */ bool experimental_upgrade; }; -static u8 *create_channel_announcement(const tal_t *ctx, struct peer *peer); static void start_commit_timer(struct peer *peer); static void billboard_update(const struct peer *peer) { const char *update = billboard_message(tmpctx, peer->channel_ready, - peer->have_sigs, + NULL, peer->shutdown_sent, peer->depth_togo, num_channel_htlcs(peer->channel)); @@ -407,272 +381,6 @@ static void set_channel_type(struct channel *channel, const u8 *type) take(towire_channeld_upgraded(NULL, channel->type))); } -/* 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, bool enable) -{ - u8 *msg; - - /* Only send an update if we told gossipd */ - if (!peer->channel_local_active) - return; - - assert(peer->short_channel_ids[LOCAL].u64); - - msg = towire_channeld_local_channel_update(NULL, enable); - 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) -{ - /* If `stfu` is already active then the channel is being mutated quickly - * after creation. These mutations (ie. splice) must announce the - * channel when they finish anyway, so it is safe to skip it here */ - if (!is_stfu_active(peer) && !peer->want_stfu) - send_channel_update(peer, true); -} - -/** - * Add a channel locally and send a channel update to the peer - * - * Send a local_add_channel message to gossipd in order to make the channel - * usable locally, and also tell our peer about our parameters via a - * channel_update message. The peer may accept the update and use the contained - * information to route incoming payments through the channel. The - * channel_update is not preceeded by a channel_announcement and won't make much - * sense to other nodes, so we don't tell gossipd about it. - */ -static void make_channel_local_active(struct peer *peer) -{ - u8 *msg; - const u8 *annfeatures = get_agreed_channelfeatures(tmpctx, - peer->our_features, - peer->their_features); - - /* 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)); - - /* 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) -{ - /* First 2 + 256 byte are the signatures and msg type, skip them */ - size_t offset = 258; - struct sha256_double hash; - const u8 *msg, *ca, *req; - struct pubkey mykey; - - status_debug("Exchanging announcement signatures."); - ca = create_channel_announcement(tmpctx, peer); - req = towire_hsmd_cannouncement_sig_req(tmpctx, ca); - - msg = hsm_req(tmpctx, req); - if (!fromwire_hsmd_cannouncement_sig_reply(msg, - &peer->announcement_node_sigs[LOCAL], - &peer->announcement_bitcoin_sigs[LOCAL])) - status_failed(STATUS_FAIL_HSM_IO, - "Reading cannouncement_sig_resp: %s", - strerror(errno)); - - /* Double-check that HSM gave valid signatures. */ - sha256_double(&hash, ca + offset, tal_count(ca) - offset); - if (!pubkey_from_node_id(&mykey, &peer->node_ids[LOCAL])) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Could not convert my id '%s' to pubkey", - type_to_string(tmpctx, struct node_id, - &peer->node_ids[LOCAL])); - if (!check_signed_hash(&hash, &peer->announcement_node_sigs[LOCAL], - &mykey)) { - /* It's ok to fail here, the channel announcement is - * unique, unlike the channel update which may have - * been replaced in the meantime. */ - status_failed(STATUS_FAIL_HSM_IO, - "HSM returned an invalid node signature"); - } - - if (!check_signed_hash(&hash, &peer->announcement_bitcoin_sigs[LOCAL], - &peer->channel->funding_pubkey[LOCAL])) { - /* It's ok to fail here, the channel announcement is - * unique, unlike the channel update which may have - * been replaced in the meantime. */ - status_failed(STATUS_FAIL_HSM_IO, - "HSM returned an invalid bitcoin signature"); - } - - msg = towire_announcement_signatures( - NULL, &peer->channel_id, &peer->short_channel_ids[LOCAL], - &peer->announcement_node_sigs[LOCAL], - &peer->announcement_bitcoin_sigs[LOCAL]); - peer_write(peer->pps, take(msg)); -} - -/* Tentatively create a channel_announcement, possibly with invalid - * signatures. The signatures need to be collected first, by asking - * the HSM and by exchanging announcement_signature messages. */ -static u8 *create_channel_announcement(const tal_t *ctx, struct peer *peer) -{ - int first, second; - u8 *cannounce, *features - = get_agreed_channelfeatures(tmpctx, peer->our_features, - peer->their_features); - - if (peer->channel_direction == 0) { - first = LOCAL; - second = REMOTE; - } else { - first = REMOTE; - second = LOCAL; - } - - cannounce = towire_channel_announcement( - ctx, &peer->announcement_node_sigs[first], - &peer->announcement_node_sigs[second], - &peer->announcement_bitcoin_sigs[first], - &peer->announcement_bitcoin_sigs[second], - features, - &chainparams->genesis_blockhash, - &peer->short_channel_ids[LOCAL], - &peer->node_ids[first], - &peer->node_ids[second], - &peer->channel->funding_pubkey[first], - &peer->channel->funding_pubkey[second]); - return cannounce; -} - -/* Once we have both, we'd better make sure we agree what they are! */ -static void check_short_ids_match(struct peer *peer) -{ - assert(peer->have_sigs[LOCAL]); - assert(peer->have_sigs[REMOTE]); - - if (!short_channel_id_eq(&peer->short_channel_ids[LOCAL], - &peer->short_channel_ids[REMOTE])) - peer_failed_warn(peer->pps, &peer->channel_id, - "We disagree on short_channel_ids:" - " I have %s, you say %s", - type_to_string(peer, struct short_channel_id, - &peer->short_channel_ids[LOCAL]), - type_to_string(peer, struct short_channel_id, - &peer->short_channel_ids[REMOTE])); -} - -static void announce_channel(struct peer *peer) -{ - u8 *cannounce; - - /* If we splice quickly enough, the initial channel announcement may - * still be pending. This old announcement is made stale by splicing, - * so we ommit it. */ - if (!peer->have_sigs[LOCAL] || !peer->have_sigs[REMOTE]) - return; - - cannounce = create_channel_announcement(tmpctx, peer); - - wire_sync_write(MASTER_FD, - take(towire_channeld_local_channel_announcement(NULL, - cannounce))); - send_channel_update(peer, true); -} - -static void announce_channel_if_not_stfu(struct peer *peer) -{ - if (!is_stfu_active(peer) && !peer->want_stfu) - announce_channel(peer); -} - -/* Returns true if an announcement was sent */ -static bool channel_announcement_negotiate(struct peer *peer) -{ - bool sent_announcement = false; - - /* Don't do any announcement work if we're shutting down */ - if (peer->shutdown_sent[LOCAL]) - return false; - - /* Can't do anything until funding is locked. */ - if (!peer->channel_ready[LOCAL] || !peer->channel_ready[REMOTE]) - return false; - - /* Don't announce channel if we're in stfu mode */ - if (peer->want_stfu || is_stfu_active(peer)) - return false; - - 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: - * - * 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 `channel_ready` - * has been sent and received AND the funding transaction has at least six confirmations. - * - otherwise: - * - MUST NOT send the `announcement_signatures` message. - */ - if (!(peer->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)) - return false; - - /* BOLT #7: - * - * - 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]) { - /* When we reenable the channel, we will also send the announcement to remote peer, and - * receive the remote announcement reply. But we will rebuild the channel with announcement - * from the DB directly, other than waiting for the remote announcement reply. - */ - send_announcement_signatures(peer); - peer->have_sigs[LOCAL] = true; - billboard_update(peer); - sent_announcement = true; - } - - /* If we've completed the signature exchange, we can send a real - * announcement, otherwise we send a temporary one */ - if (peer->have_sigs[LOCAL] && peer->have_sigs[REMOTE]) { - check_short_ids_match(peer); - - /* After making sure short_channel_ids match, we can send remote - * announcement to MASTER. */ - wire_sync_write(MASTER_FD, - take(towire_channeld_got_announcement(NULL, - &peer->short_channel_ids[REMOTE], - &peer->announcement_node_sigs[REMOTE], - &peer->announcement_bitcoin_sigs[REMOTE]))); - - /* Give other nodes time to notice new block. */ - notleak(new_reltimer(&peer->timers, peer, - time_from_sec(GOSSIP_ANNOUNCE_DELAY(peer->dev_fast_gossip)), - announce_channel_if_not_stfu, peer)); - } - - return sent_announcement; -} - static void lock_signer_outpoint(const struct bitcoin_outpoint *outpoint) { const u8 *msg; @@ -740,10 +448,6 @@ static void check_mutual_splice_locked(struct peer *peer) peer->splice_state->locked_ready[LOCAL] = false; peer->splice_state->locked_ready[REMOTE] = false; - peer->have_sigs[LOCAL] = false; - peer->have_sigs[REMOTE] = false; - peer->send_duplicate_announce_sigs = true; - peer->splice_state->last_short_channel_id = peer->short_channel_ids[LOCAL]; peer->short_channel_ids[LOCAL] = peer->splice_state->short_channel_id; peer->short_channel_ids[REMOTE] = peer->splice_state->short_channel_id; @@ -793,11 +497,6 @@ static void check_mutual_splice_locked(struct peer *peer) &inflight->outpoint.txid); wire_sync_write(MASTER_FD, take(msg)); - /* We must regossip the scid since it has changed */ - peer->gossip_scid_announced = false; - - if (channel_announcement_negotiate(peer)) - send_channel_update(peer, true); billboard_update(peer); peer->splice_state->inflights = tal_free(peer->splice_state->inflights); @@ -880,115 +579,29 @@ static void handle_peer_channel_ready(struct peer *peer, const u8 *msg) take(towire_channeld_got_channel_ready( NULL, &peer->remote_per_commit, tlvs->short_channel_id))); - channel_announcement_negotiate(peer); billboard_update(peer); - peer->send_duplicate_announce_sigs = true; -} - -/* Checks that key is valid, and signed this hash - * - * FIXME: move this inside common/utils.h */ -static bool check_signed_hash_nodeid(const struct sha256_double *hash, - const secp256k1_ecdsa_signature *signature, - const struct node_id *id) -{ - struct pubkey key; - - return pubkey_from_node_id(&key, id) - && check_signed_hash(hash, signature, &key); } static void handle_peer_announcement_signatures(struct peer *peer, const u8 *msg) { - const u8 *cannounce; struct channel_id chanid; - struct sha256_double hash; struct short_channel_id remote_scid; + secp256k1_ecdsa_signature remote_node_sig, remote_bitcoin_sig; if (!fromwire_announcement_signatures(msg, &chanid, &remote_scid, - &peer->announcement_node_sigs[REMOTE], - &peer->announcement_bitcoin_sigs[REMOTE])) + &remote_node_sig, + &remote_bitcoin_sig)) peer_failed_warn(peer->pps, &peer->channel_id, "Bad announcement_signatures %s", tal_hex(msg, msg)); - /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: - * Once a node has received and sent `splice_locked`: - * - Until sending OR receiving of `revoke_and_ack` - * - MUST ignore `announcement_signatures` messages where - * `short_channel_id` matches the pre-splice short channel id. */ - if (peer->splice_state->await_commitment_succcess - && !short_channel_id_eq(&remote_scid, - &peer->short_channel_ids[LOCAL])) { - status_info("Ignoring stale announcement_signatures: expected" - " %s, got %s", - type_to_string(tmpctx, struct short_channel_id, - &peer->short_channel_ids[LOCAL]), - type_to_string(tmpctx, struct short_channel_id, - &remote_scid)); - return; - } - - peer->short_channel_ids[REMOTE] = remote_scid; - - /* Make sure we agree on the channel ids */ - if (!channel_id_eq(&chanid, &peer->channel_id)) { - peer_failed_err(peer->pps, &chanid, - "Wrong channel_id: expected %s, got %s", - type_to_string(tmpctx, struct channel_id, - &peer->channel_id), - type_to_string(tmpctx, struct channel_id, &chanid)); - } - - /* BOLT 7: - * - if the node_signature OR the bitcoin_signature is NOT correct: - * - MAY send a warning and close the connection, or send an error and fail the channel. - * - * In our case, we send an error and stop the open channel procedure. This approach is - * considered overly strict since the peer can recover from it. However, this step is - * optional. If the peer sends it, we assume that the signature must be correct.*/ - cannounce = create_channel_announcement(tmpctx, peer); - - /* 2 byte msg type + 256 byte signatures */ - int offset = 258; - sha256_double(&hash, cannounce + offset, - tal_count(cannounce) - offset); - - if (!check_signed_hash_nodeid(&hash, &peer->announcement_node_sigs[REMOTE], &peer->node_ids[REMOTE])) { - peer_failed_warn(peer->pps, &chanid, - "Bad node_signature %s hash %s" - " on announcement_signatures %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - &peer->announcement_node_sigs[REMOTE]), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, cannounce)); - } - if (!check_signed_hash(&hash, &peer->announcement_bitcoin_sigs[REMOTE], &peer->channel->funding_pubkey[REMOTE])) { - peer_failed_warn(peer->pps, &chanid, - "Bad bitcoin_signature %s hash %s" - " on announcement_signatures %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - &peer->announcement_bitcoin_sigs[REMOTE]), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, cannounce)); - } - peer->have_sigs[REMOTE] = true; - billboard_update(peer); - - if (!channel_announcement_negotiate(peer) - && peer->send_duplicate_announce_sigs - && peer->have_sigs[LOCAL]) { - peer->send_duplicate_announce_sigs = false; - send_announcement_signatures(peer); - } + wire_sync_write(MASTER_FD, + take(towire_channeld_got_announcement(NULL, + &remote_scid, + &remote_node_sig, + &remote_bitcoin_sig))); } static void handle_peer_add_htlc(struct peer *peer, const u8 *msg) @@ -1230,10 +843,6 @@ static void maybe_send_shutdown(struct peer *peer) /* DTODO: Ensure 'shutdown' rules around splice are followed once those * rules get settled on spec */ - /* Send a disable channel_update so others don't try to route - * over us */ - send_channel_update(peer, false); - if (peer->shutdown_wrong_funding) { tlvs = tlv_shutdown_tlvs_new(tmpctx); tlvs->wrong_funding @@ -2613,9 +2222,6 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) /* DTODO: Ensure `shutdown` follows new splice related rules once * completed in the spec */ - /* Disable the channel. */ - send_channel_update(peer, false); - if (!fromwire_shutdown(tmpctx, shutdown, &channel_id, &scriptpubkey, &tlvs)) peer_failed_warn(peer->pps, &peer->channel_id, @@ -3764,8 +3370,6 @@ static void resume_splice_negotiation(struct peer *peer, msg = towire_channeld_splice_confirmed_signed(tmpctx, final_tx, chan_output_index); wire_sync_write(MASTER_FD, take(msg)); - - send_channel_update(peer, true); } } @@ -5641,12 +5245,6 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) peer->splice_state->locked_ready[LOCAL] = true; check_mutual_splice_locked(peer); } - - peer->announce_depth_reached = (depth >= ANNOUNCE_MIN_DEPTH); - - /* Send temporary or final announcements */ - if (!splicing) - channel_announcement_negotiate(peer); } billboard_update(peer); @@ -6070,9 +5668,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_LOCAL_CHANNEL_UPDATE: - case WIRE_CHANNELD_LOCAL_CHANNEL_ANNOUNCEMENT: - case WIRE_CHANNELD_LOCAL_PRIVATE_CHANNEL: case WIRE_CHANNELD_ADD_INFLIGHT: case WIRE_CHANNELD_UPDATE_INFLIGHT: case WIRE_CHANNELD_GOT_INFLIGHT: @@ -6103,8 +5698,6 @@ static void init_channel(struct peer *peer) struct height_states *blockheight_states; u32 minimum_depth, lease_expiry; struct secret last_remote_per_commit_secret; - secp256k1_ecdsa_signature *remote_ann_node_sig; - secp256k1_ecdsa_signature *remote_ann_bitcoin_sig; struct penalty_base *pbases; bool reestablish_only; struct channel_type *channel_type; @@ -6137,8 +5730,6 @@ static void init_channel(struct peer *peer) &local_msat, &points[LOCAL], &funding_pubkey[LOCAL], - &peer->node_ids[LOCAL], - &peer->node_ids[REMOTE], &peer->commit_msec, &peer->last_was_revoke, &peer->last_sent_commit, @@ -6158,14 +5749,10 @@ static void init_channel(struct peer *peer) &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, - &peer->dev_fast_gossip, &peer->dev_disable_commit, &pbases, &reestablish_only, @@ -6216,20 +5803,6 @@ static void init_channel(struct peer *peer) type_to_string(tmpctx, struct height_states, blockheight_states), peer->our_blockheight); - if (remote_ann_node_sig && remote_ann_bitcoin_sig) { - peer->announcement_node_sigs[REMOTE] = *remote_ann_node_sig; - peer->announcement_bitcoin_sigs[REMOTE] = *remote_ann_bitcoin_sig; - peer->have_sigs[REMOTE] = true; - - /* Before we store announcement into DB, we have made sure - * remote short_channel_id matched the local. Now we initial - * it directly! - */ - peer->short_channel_ids[REMOTE] = peer->short_channel_ids[LOCAL]; - tal_free(remote_ann_node_sig); - tal_free(remote_ann_bitcoin_sig); - } - /* First commit is used for opening: if we've sent 0, we're on * index 1. */ assert(peer->next_index[LOCAL] > 0); @@ -6265,9 +5838,6 @@ static void init_channel(struct peer *peer) update_view_from_inflights(peer); - peer->channel_direction = node_id_idx(&peer->node_ids[LOCAL], - &peer->node_ids[REMOTE]); - /* Default desired feerate is the feerate we set for them last. */ if (peer->channel->opener == LOCAL) peer->desired_feerate = channel_feerate(peer->channel, REMOTE); @@ -6286,9 +5856,6 @@ static void init_channel(struct peer *peer) if (fwd_msg) peer_write(peer->pps, take(fwd_msg)); - /* Reenable channel */ - channel_announcement_negotiate(peer); - billboard_update(peer); } @@ -6296,7 +5863,7 @@ int main(int argc, char *argv[]) { setup_locale(); - int i, nfds; + int nfds; fd_set fds_in, fds_out; struct peer *peer; bool developer; @@ -6309,16 +5876,11 @@ int main(int argc, char *argv[]) peer->developer = developer; timers_init(&peer->timers, time_mono()); peer->commit_timer = NULL; - 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; peer->last_update_timestamp = 0; peer->last_empty_commitment = 0; - peer->send_duplicate_announce_sigs = false; peer->want_stfu = false; peer->stfu_sent[LOCAL] = peer->stfu_sent[REMOTE] = false; peer->stfu_wait_single_msg = false; @@ -6327,15 +5889,6 @@ int main(int argc, char *argv[]) peer->splice_state = splice_state_new(peer); peer->splicing = NULL; - /* We send these to HSM to get real signatures; don't have valgrind - * complain. */ - for (i = 0; i < NUM_SIDES; i++) { - memset(&peer->announcement_node_sigs[i], 0, - sizeof(peer->announcement_node_sigs[i])); - memset(&peer->announcement_bitcoin_sigs[i], 0, - sizeof(peer->announcement_bitcoin_sigs[i])); - } - /* Prepare the ecdh() function for use */ ecdh_hsmd_setup(HSM_FD, status_failed); diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index b8bbb7ae17e7..40ec06215cd7 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -39,8 +39,6 @@ msgdata,channeld_init,opener,enum side, msgdata,channeld_init,local_msatoshi,amount_msat, msgdata,channeld_init,our_basepoints,basepoints, msgdata,channeld_init,our_funding_pubkey,pubkey, -msgdata,channeld_init,local_node_id,node_id, -msgdata,channeld_init,remote_node_id,node_id, msgdata,channeld_init,commit_msec,u32, msgdata,channeld_init,last_was_revoke,bool, msgdata,channeld_init,num_last_sent_commit,u16, @@ -64,16 +62,12 @@ msgdata,channeld_init,final_scriptpubkey,u8,final_scriptpubkey_len msgdata,channeld_init,flags,u8, msgdata,channeld_init,init_peer_pkt_len,u16, msgdata,channeld_init,init_peer_pkt,u8,init_peer_pkt_len -msgdata,channeld_init,reached_announce_depth,bool, msgdata,channeld_init,last_remote_secret,secret, msgdata,channeld_init,flen,u16, msgdata,channeld_init,their_features,u8,flen msgdata,channeld_init,upfront_shutdown_script_len,u16, msgdata,channeld_init,upfront_shutdown_script,u8,upfront_shutdown_script_len -msgdata,channeld_init,remote_ann_node_sig,?secp256k1_ecdsa_signature, -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_disable_commit,?u32, msgdata,channeld_init,num_penalty_bases,u32, msgdata,channeld_init,pbases,penalty_base,num_penalty_bases @@ -334,21 +328,6 @@ msgdata,channeld_send_error,reason,wirestring, # Tell master channeld has sent the error message. msgtype,channeld_send_error_reply,1108 -# Channeld: tell gossipd to make channel_update to enable/disable. -msgtype,channeld_local_channel_update,1013 -msgdata,channeld_local_channel_update,enable,bool, - -# 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/lightningd/channel_control.c b/lightningd/channel_control.c index 99bf5ba99211..bd2c84d0cb09 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1354,11 +1354,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; - /* FIXME: remove from channeld_wire.csv */ - case WIRE_CHANNELD_LOCAL_CHANNEL_UPDATE: - case WIRE_CHANNELD_LOCAL_CHANNEL_ANNOUNCEMENT: - case WIRE_CHANNELD_LOCAL_PRIVATE_CHANNEL: - break; case WIRE_CHANNELD_SPLICE_CONFIRMED_INIT: handle_splice_confirmed_init(sd->ld, sd->channel, msg); break; @@ -1440,9 +1435,8 @@ bool peer_start_channeld(struct channel *channel, u64 num_revocations; struct lightningd *ld = channel->peer->ld; const struct config *cfg = &ld->config; - bool reached_announce_depth; struct secret last_remote_per_commit_secret; - secp256k1_ecdsa_signature *remote_ann_node_sig, *remote_ann_bitcoin_sig; struct penalty_base *pbases; + struct penalty_base *pbases; u32 min_feerate, max_feerate, curr_blockheight; struct channel_inflight *inflight; struct inflight **inflights; @@ -1484,16 +1478,9 @@ bool peer_start_channeld(struct channel *channel, if (channel->scid) { scid = *channel->scid; - reached_announce_depth - = is_scid_depth_announceable(&scid, - get_block_height(ld->topology)); - log_debug(channel->log, "Already have funding locked in%s", - reached_announce_depth - ? " (and ready to announce)" : ""); + log_debug(channel->log, "Already have funding locked in"); } else { - log_debug(channel->log, "Waiting for funding confirmations"); memset(&scid, 0, sizeof(scid)); - reached_announce_depth = false; } num_revocations = revocations_received(&channel->their_shachain.chain); @@ -1522,17 +1509,6 @@ bool peer_start_channeld(struct channel *channel, if (channel->ignore_fee_limits || ld->config.ignore_fee_limits) log_unusual(channel->log, "Ignoring fee limits!"); - remote_ann_node_sig = tal(tmpctx, secp256k1_ecdsa_signature); - remote_ann_bitcoin_sig = tal(tmpctx, secp256k1_ecdsa_signature); - - if (!wallet_remote_ann_sigs_load(channel->peer->ld->wallet, - channel, - remote_ann_node_sig, - remote_ann_bitcoin_sig)) { - remote_ann_node_sig = tal_free(remote_ann_node_sig); - remote_ann_bitcoin_sig = tal_free(remote_ann_bitcoin_sig); - } - pbases = wallet_penalty_base_load_for_channel( tmpctx, channel->peer->ld->wallet, channel->dbid); @@ -1633,8 +1609,6 @@ bool peer_start_channeld(struct channel *channel, channel->our_msat, &channel->local_basepoints, &channel->local_funding_pubkey, - &ld->id, - &channel->peer->id, cfg->commit_time_ms, channel->last_was_revoke, channel->last_sent_commit, @@ -1656,14 +1630,10 @@ bool peer_start_channeld(struct channel *channel, 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, - NULL, - NULL, channel->type, - ld->dev_fast_gossip, ld->dev_disable_commit == -1 ? NULL : (u32 *)&ld->dev_disable_commit, From 4db58a5a7fb5088962b3dbab534458bcf234c4c3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 15/38] dualopend: remove channel_update message creation. We don't have to generate these any more: lightningd does it. Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 3 --- openingd/dualopend.c | 36 ---------------------------------- openingd/dualopend_wire.csv | 6 ------ 3 files changed, 45 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 29bd26503de4..d89138d32ad3 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -3629,9 +3629,6 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND: channel_fail_fallen_behind(dualopend, msg); return 0; - /* FIXME: remove from dual_open_wire.csv */ - case WIRE_DUALOPEND_LOCAL_PRIVATE_CHANNEL: - return 0; case WIRE_DUALOPEND_VALIDATE_INPUTS: handle_validate_inputs(dualopend, msg); return 0; diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 407105967079..8d1cf58d270c 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3882,38 +3882,6 @@ static void send_channel_ready(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; @@ -3928,9 +3896,6 @@ 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_channel_ready(state); if (state->channel_ready[REMOTE]) return towire_dualopend_channel_locked(state); @@ -4211,7 +4176,6 @@ 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: case WIRE_DUALOPEND_VALIDATE_INPUTS: break; } diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 4a7f8a4803b4..01c8b70c3bf6 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -282,9 +282,3 @@ 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 004d032d0b844cf4673e0addc5fcfec2f0ff175e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 16/38] gossipd: no longer take private channel updates from lightningd Lightningd now handles private channels, so we're dismantling the gossipd infrastructure. Signed-off-by: Rusty Russell --- gossipd/gossip_store.c | 4 ---- gossipd/gossipd.c | 45 ------------------------------------- gossipd/gossipd_wire.csv | 9 -------- lightningd/gossip_control.c | 1 - 4 files changed, 59 deletions(-) diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index ed33de947db9..bf1c01d0e5af 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -963,10 +963,6 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) } stats[2]++; break; - /* FIXME: Don't actually put these in! */ - case WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS: - case WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS: - break; default: bad = "Unknown message"; goto badmsg; diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 35015ec49012..49d75a4fd2fd 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -331,47 +331,6 @@ static void handle_local_channel_announcement(struct daemon *daemon, const u8 *m } -/* 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; - struct chan *zombie; - - if (!fromwire_gossipd_local_private_channel(msg, msg, - &id, &capacity, &scid, - &features)) - master_badmsg(WIRE_GOSSIPD_LOCAL_PRIVATE_CHANNEL, msg); - - status_debug("received private channel announcement from channeld for %s", - type_to_string(tmpctx, struct short_channel_id, &scid)); - cannounce = private_channel_announcement(tmpctx, - &scid, - &daemon->id, - &id, - features); - /* If there is already a zombie announcement for this channel in the - * store we can disregard this one. */ - zombie = get_channel(daemon->rstate, &scid); - if (zombie && (zombie->half[0].zombie || zombie->half[1].zombie)){ - status_debug("received channel announcement for %s," - " but it is a zombie; discarding", - type_to_string(tmpctx, struct short_channel_id, - &scid)); - return; - } - - if (!routing_add_private_channel(daemon->rstate, &id, capacity, - cannounce, 0)) { - status_peer_broken(&id, "bad add_private_channel %s", - tal_hex(tmpctx, cannounce)); - } -} - /* lightningd tells us it has discovered and verified new `remote_addr`. * We can use this to update our node announcement. */ static void handle_discovered_ip(struct daemon *daemon, const u8 *msg) @@ -1164,10 +1123,6 @@ static struct io_plan *recv_req(struct io_conn *conn, handle_local_channel_announcement(daemon, msg); goto done; - case WIRE_GOSSIPD_LOCAL_PRIVATE_CHANNEL: - handle_local_private_channel(daemon, msg); - goto done; - case WIRE_GOSSIPD_DISCOVERED_IP: handle_discovered_ip(daemon, msg); goto done; diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 0dd73b6696fc..7a72606cb7ca 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -120,15 +120,6 @@ 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/lightningd/gossip_control.c b/lightningd/gossip_control.c index e73bdc9d43f8..36933141e0b0 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -171,7 +171,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT: case WIRE_GOSSIPD_USED_LOCAL_CHANNEL_UPDATE: case WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE: - 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: From 47bdbdaa0aca206637852a937c457ebdf3c8cbe9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 17/38] gossipd: don't put private channel info into store at all. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 28 +-- gossipd/gossip_store.c | 23 -- gossipd/gossip_store.h | 14 -- gossipd/gossipd.c | 27 --- gossipd/queries.c | 7 +- gossipd/routing.c | 229 ++---------------- gossipd/routing.h | 20 -- gossipd/seeker.c | 4 - gossipd/test/run-check_channel_announcement.c | 12 - gossipd/test/run-check_node_announcement.c | 6 - gossipd/test/run-crc32_of_update.c | 3 - gossipd/test/run-txout_failure.c | 12 - 12 files changed, 26 insertions(+), 359 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 29787231e1a3..619090e896df 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -508,11 +508,9 @@ static u8 *sign_and_timestamp_update(const tal_t *ctx, /* Tell lightningd about this immediately (even if we're not actually * 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)); - } + msg = towire_gossipd_got_local_channel_update(NULL, &chan->scid, + update); + daemon_conn_send(daemon->master, take(msg)); return update; } @@ -584,21 +582,6 @@ static void apply_update(struct daemon *daemon, u8 *update TAKES) { u8 *msg; - struct peer *peer = find_peer(daemon, &chan->nodes[!direction]->id); - - 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 */ - if (peer) - queue_peer_msg(peer, update); - - if (update_taken) - take(update); - } msg = handle_channel_update(daemon->rstate, update, NULL, NULL, true); if (msg) @@ -719,9 +702,6 @@ static u8 *prev_update(const tal_t *ctx, gossip_store_get(tmpctx, daemon->rstate->gs, chan->half[direction].bcast.index)); - /* If it's a private update, unwrap */ - if (!fromwire_gossip_store_private_update_obs(ctx, prev, &prev)) - tal_steal(ctx, prev); return prev; } @@ -851,7 +831,7 @@ void handle_local_channel_update(struct daemon *daemon, const u8 *msg) return; /* Too early? Defer (don't worry if it's unannounced). */ - if (is_halfchan_defined(hc) && is_chan_public(chan)) { + if (is_halfchan_defined(hc)) { u32 now = time_now().ts.tv_sec; u32 next_time = hc->bcast.timestamp + GOSSIP_MIN_INTERVAL(daemon->rstate->dev_fast_gossip); diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index bf1c01d0e5af..2c03719d54d7 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -633,14 +632,6 @@ u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, return off; } -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_obs(tmpctx, update); - return gossip_store_add(gs, pupdate, 0, false, false, false, NULL); -} - void gossip_store_mark_dying(struct gossip_store *gs, const struct broadcastable *bcast, int type) @@ -836,20 +827,6 @@ const u8 *gossip_store_get(const tal_t *ctx, return msg; } -const u8 *gossip_store_get_private_update(const tal_t *ctx, - struct gossip_store *gs, - u64 offset) -{ - const u8 *pmsg = gossip_store_get(tmpctx, gs, offset); - u8 *msg; - - if (!fromwire_gossip_store_private_update_obs(ctx, pmsg, &msg)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed to decode private update @%"PRIu64": %s", - offset, tal_hex(tmpctx, pmsg)); - return msg; -} - int gossip_store_readonly_fd(struct gossip_store *gs) { int fd = open(GOSSIP_STORE_FILENAME, O_RDONLY); diff --git a/gossipd/gossip_store.h b/gossipd/gossip_store.h index a8d7d7bc6888..b318842f150a 100644 --- a/gossipd/gossip_store.h +++ b/gossipd/gossip_store.h @@ -28,11 +28,6 @@ struct gossip_store *gossip_store_new(struct routing_state *rstate); */ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs); -/** - * Add a private channel_update message to the gossip_store - */ -u64 gossip_store_add_private_update(struct gossip_store *gs, const u8 *update); - /** * Add a gossip message to the gossip_store (and optional addendum) * @gs: gossip store @@ -96,15 +91,6 @@ const u8 *gossip_store_get(const tal_t *ctx, struct gossip_store *gs, u64 offset); -/** - * Direct store accessor: loads private gossip msg back from store. - * - * Caller must ensure offset != 0. Never returns NULL. - */ -const u8 *gossip_store_get_private_update(const tal_t *ctx, - struct gossip_store *gs, - u64 offset); - /* Exposed for dev-compact-gossip-store to force compaction. */ bool gossip_store_compact(struct gossip_store *gs); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 49d75a4fd2fd..fe4ad3617b72 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -146,15 +145,6 @@ void queue_peer_from_store(struct peer *peer, queue_peer_msg(peer, take(gossip_store_get(NULL, gs, bcast->index))); } -static void queue_priv_update(struct peer *peer, - const struct broadcastable *bcast) -{ - struct gossip_store *gs = peer->daemon->rstate->gs; - queue_peer_msg(peer, - take(gossip_store_get_private_update(NULL, gs, - bcast->index))); -} - /*~ We don't actually keep node_announcements in memory; we keep them in * a file called `gossip_store`. If we need some node details, we reload * and reparse. It's slow, but generally rare. */ @@ -402,19 +392,6 @@ static void dump_our_gossip(struct daemon *daemon, struct peer *peer) return; for (chan = first_chan(me, &it); chan; chan = next_chan(me, &it)) { - /* Don't leak private channels, unless it's with you! */ - if (!is_chan_public(chan)) { - int dir = half_chan_idx(me, chan); - - if (node_id_eq(&chan->nodes[!dir]->id, &peer->id) - && is_halfchan_defined(&chan->half[dir])) { - /* There's no announce for this, of course! */ - /* Private channel updates are wrapped in the store. */ - queue_priv_update(peer, &chan->half[dir].bcast); - } - continue; - } - tal_arr_expand(&chans, chan); } @@ -752,10 +729,6 @@ static void tell_master_local_cupdates(struct daemon *daemon) int direction; const u8 *cupdate; - /* We don't provide update_channel for unannounced channels */ - if (!is_chan_public(c)) - continue; - if (!local_direction(daemon->rstate, c, &direction)) continue; diff --git a/gossipd/queries.c b/gossipd/queries.c index d8b658862272..5f6f3accd305 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -351,7 +351,7 @@ static void get_checksum_and_timestamp(struct routing_state *rstate, { const struct half_chan *hc = &chan->half[direction]; - if (!is_chan_public(chan) || !is_halfchan_defined(hc)) { + if (!is_halfchan_defined(hc)) { *tstamp = *csum = 0; } else { const u8 *update = gossip_store_get(tmpctx, rstate->gs, @@ -468,9 +468,6 @@ static struct short_channel_id *gather_range(const tal_t *ctx, /* FIXME: Store csum in header. */ chan = get_channel(rstate, &scid); - if (!is_chan_public(chan)) - continue; - tal_arr_expand(&scids, scid); /* Don't calc csums if we don't even care */ @@ -923,7 +920,7 @@ static bool maybe_send_query_responses_peer(struct peer *peer) struct chan *chan; chan = get_channel(rstate, &peer->scid_queries[i]); - if (!chan || !is_chan_public(chan)) + if (!chan) continue; /* BOLT #7: diff --git a/gossipd/routing.c b/gossipd/routing.c index c1adad4bc142..1ad4905d0270 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -389,20 +389,6 @@ static bool is_chan_zombie(struct chan *chan) return false; } -/* We've received a channel_announce for a channel attached to this node: - * otherwise it's in the map only because it's a peer, or us. */ -static bool node_has_public_channels(struct node *node) -{ - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - if (is_chan_public(c) && !is_chan_zombie(c)) - return true; - } - return false; -} - static bool is_node_zombie(struct node* node) { struct chan_map_iter i; @@ -423,8 +409,6 @@ bool node_has_broadcastable_channels(const struct node *node) struct chan *c; for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - if (!is_chan_public(c)) - continue; if (is_chan_zombie(c)) continue; if (is_halfchan_defined(&c->half[0]) @@ -440,9 +424,6 @@ static bool node_announce_predates_channels(const struct node *node) struct chan *c; for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - if (!is_chan_public(c)) - continue; - /* Zombies don't count! */ if (is_chan_zombie(c)) continue; @@ -760,11 +741,9 @@ static void catch_node_announcement(const tal_t *ctx, struct pending_node_announce *pna; struct node *node; - /* No need if we already know about the node. We might, however, only - * know about it because it's a peer (maybe with private or - * not-yet-announced channels), so check for that too. */ + /* No need if we already know about the node. */ node = get_node(rstate, nodeid); - if (node && node_has_public_channels(node)) + if (node) return; /* We can have multiple channels announced at same time for nodes; @@ -878,29 +857,19 @@ static void add_channel_announce_to_broadcast(struct routing_state *rstate, 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_OBS; - announcment_type = WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS; - } - /* If these aren't in the store, these are noops. */ gossip_store_delete(rstate->gs, - &chan->bcast, announcment_type); + &chan->bcast, WIRE_CHANNEL_ANNOUNCEMENT); if (chan->half[0].rgraph.index != chan->half[0].bcast.index) gossip_store_delete(rstate->gs, - &chan->half[0].rgraph, update_type); + &chan->half[0].rgraph, WIRE_CHANNEL_UPDATE); gossip_store_delete(rstate->gs, - &chan->half[0].bcast, update_type); + &chan->half[0].bcast, WIRE_CHANNEL_UPDATE); if (chan->half[1].rgraph.index != chan->half[1].bcast.index) gossip_store_delete(rstate->gs, - &chan->half[1].rgraph, update_type); + &chan->half[1].rgraph, WIRE_CHANNEL_UPDATE); gossip_store_delete(rstate->gs, - &chan->half[1].bcast, update_type); + &chan->half[1].bcast, WIRE_CHANNEL_UPDATE); } static void remove_channel_from_store(struct routing_state *rstate, @@ -920,7 +889,6 @@ bool routing_add_channel_announcement(struct routing_state *rstate, u32 index, const struct node_id *source_peer) { - struct chan *oldchan; secp256k1_ecdsa_signature node_signature_1, node_signature_2; secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; u8 *features; @@ -931,7 +899,6 @@ bool routing_add_channel_announcement(struct routing_state *rstate, struct pubkey bitcoin_key_1; struct pubkey bitcoin_key_2; struct unupdated_channel *uc; - const u8 *private_updates[2] = { NULL, NULL }; /* Make sure we own msg, even if we don't save it. */ if (taken(msg)) @@ -943,40 +910,6 @@ bool routing_add_channel_announcement(struct routing_state *rstate, &scid, &node_id_1, &node_id_2, &bitcoin_key_1, &bitcoin_key_2)) return false; - /* 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. */ - 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 (oldchan) { - /* If this was in the gossip_store, gossip_store is bad! */ - if (index) { - status_broken("gossip_store channel_announce" - " %u replaces %u!", - index, oldchan->bcast.index); - return false; - } - - /* Reload any private updates */ - if (oldchan->half[0].bcast.index) - private_updates[0] - = gossip_store_get_private_update(NULL, - rstate->gs, - oldchan->half[0].bcast.index); - if (oldchan->half[1].bcast.index) - private_updates[1] - = gossip_store_get_private_update(NULL, - rstate->gs, - oldchan->half[1].bcast.index); - - /* 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); uc->channel_announce = tal_dup_talarr(uc, u8, msg); uc->added = gossip_time_now(rstate); @@ -993,23 +926,6 @@ bool routing_add_channel_announcement(struct routing_state *rstate, catch_node_announcement(uc, rstate, &node_id_1); catch_node_announcement(uc, rstate, &node_id_2); - /* 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, - source_peer, false, false, false); - if (private_updates[1]) - routing_add_channel_update(rstate, take(private_updates[1]), 0, - source_peer, false, false, 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; } @@ -1083,7 +999,7 @@ u8 *handle_channel_announcement(struct routing_state *rstate, /* Check if we know the channel already (no matter in what * state, we stop here if yes). */ chan = get_channel(rstate, &pending->short_channel_id); - if (chan != NULL && is_chan_public(chan)) { + if (chan != NULL) { SUPERVERBOSE("%s: %s already has public channel", __func__, type_to_string(tmpctx, struct short_channel_id, @@ -1325,16 +1241,13 @@ static void update_pending(struct pending_cannouncement *pending, } static void delete_spam_update(struct routing_state *rstate, - struct half_chan *hc, - bool update_is_public) + struct half_chan *hc) { /* Spam updates will have a unique rgraph index */ if (hc->rgraph.index == hc->bcast.index) return; gossip_store_delete(rstate->gs, &hc->rgraph, - update_is_public - ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); + WIRE_CHANNEL_UPDATE); hc->rgraph.index = hc->bcast.index; hc->rgraph.timestamp = hc->bcast.timestamp; } @@ -1502,11 +1415,8 @@ bool routing_add_channel_update(struct routing_state *rstate, return true; } - /* Make sure it's not spamming us (private channel - * updates are never considered spam, nor is anything - * we send ourselves!) */ - if (is_chan_public(chan) - && !local_direction(rstate, chan, NULL) + /* Make sure it's not spamming us */ + if (!local_direction(rstate, chan, NULL) && !ratelimit(rstate, &hc->tokens, hc->bcast.timestamp, timestamp)) { status_peer_debug(source_peer, @@ -1528,12 +1438,10 @@ bool routing_add_channel_update(struct routing_state *rstate, spam = true; /* Delete any prior entries (noop if they don't exist) */ - delete_spam_update(rstate, hc, is_chan_public(chan)); + delete_spam_update(rstate, hc); if (!spam) gossip_store_delete(rstate->gs, &hc->bcast, - is_chan_public(chan) - ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); + WIRE_CHANNEL_UPDATE); /* Update timestamp(s) */ hc->rgraph.timestamp = timestamp; @@ -1546,8 +1454,9 @@ bool routing_add_channel_update(struct routing_state *rstate, tell_lightningd_peer_update(rstate, /* Note: we can get public * channel_updates from other than - * direct peer! */ - is_chan_public(chan) ? NULL : source_peer, + * direct peer! So tell lightningd + * to trust us. */ + NULL, short_channel_id, fee_base_msat, fee_proportional_millionths, expiry, htlc_minimum, @@ -1565,23 +1474,6 @@ bool routing_add_channel_update(struct routing_state *rstate, uc->channel_announce, timestamp, uc->index); - } 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(local_direction(rstate, chan, NULL)); - /* Don't save if we're loading from store */ - if (!index) { - hc->bcast.index - = gossip_store_add_private_update(rstate->gs, - update); - /* 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; } /* Handle resurrection of zombie channels if the other side of the @@ -1608,9 +1500,7 @@ bool routing_add_channel_update(struct routing_state *rstate, zombie_addendum = gossip_store_get(tmpctx, rstate->gs, chan->bcast.index + offset); gossip_store_delete(rstate->gs, &chan->bcast, - is_chan_public(chan) - ? WIRE_CHANNEL_ANNOUNCEMENT - : WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS); + WIRE_CHANNEL_ANNOUNCEMENT); chan->bcast.index = gossip_store_add(rstate->gs, zombie_announcement, chan->bcast.timestamp, @@ -1626,14 +1516,10 @@ bool routing_add_channel_update(struct routing_state *rstate, zombie_update[1] = gossip_store_get(tmpctx, rstate->gs, chan->half[!direction].rgraph.index); gossip_store_delete(rstate->gs, &chan->half[!direction].rgraph, - is_chan_public(chan) - ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); + WIRE_CHANNEL_UPDATE); } gossip_store_delete(rstate->gs, &chan->half[!direction].bcast, - is_chan_public(chan) - ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS); + WIRE_CHANNEL_UPDATE); chan->half[!direction].bcast.index = gossip_store_add(rstate->gs, zombie_update[0], chan->half[!direction].bcast.timestamp, @@ -2122,9 +2008,6 @@ void route_prune(struct routing_state *rstate) for (struct chan *chan = uintmap_first(&rstate->chanmap, &idx); chan; chan = uintmap_after(&rstate->chanmap, &idx)) { - /* Local-only? Don't prune. */ - if (!is_chan_public(chan)) - continue; /* These have been pruned already */ if (is_chan_zombie(chan)) continue; @@ -2170,78 +2053,6 @@ void route_prune(struct routing_state *rstate) } } -bool routing_add_private_channel(struct routing_state *rstate, - 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 chan *chan; - u8 *features; - secp256k1_ecdsa_signature ignoresig; - struct bitcoin_blkid chain_hash; - - if (!fromwire_channel_announcement(tmpctx, chan_ann, - &ignoresig, - &ignoresig, - &ignoresig, - &ignoresig, - &features, - &chain_hash, - &scid, - &node_id[0], - &node_id[1], - &ignorekey, - &ignorekey)) - return false; - - /* Happens on channeld restart. */ - if (get_channel(rstate, &scid)) - return true; - - /* Make sure this id (if any) was allowed to create this */ - if (id) { - struct node_id expected[2]; - int cmp = node_id_cmp(&rstate->daemon->id, id); - - if (cmp < 0) { - expected[0] = rstate->daemon->id; - expected[1] = *id; - } else if (cmp > 0) { - expected[0] = *id; - expected[1] = rstate->daemon->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], capacity); - if (!index) { - u8 *msg = towire_gossip_store_private_channel_obs(tmpctx, - capacity, - chan_ann); - index = gossip_store_add(rstate->gs, msg, 0, false, false, false, - NULL); - } - chan->bcast.index = index; - return true; -} - struct timeabs gossip_time_now(const struct routing_state *rstate) { if (rstate->dev_gossip_time) diff --git a/gossipd/routing.h b/gossipd/routing.h index baef0d11698c..87f253743892 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -69,13 +69,6 @@ struct local_chan { /* Use this instead of tal_free(chan)! */ void free_chan(struct routing_state *rstate, struct chan *chan); -/* A local channel can exist which isn't announced: we abuse timestamp - * to indicate this. */ -static inline bool is_chan_public(const struct chan *chan) -{ - return chan->bcast.timestamp != 0; -} - static inline bool is_halfchan_defined(const struct half_chan *hc) { return hc->bcast.index != 0; @@ -379,19 +372,6 @@ bool routing_add_node_announcement(struct routing_state *rstate, bool *was_unknown, bool force_spam_flag); - -/** - * Add a local channel. - * - * Entrypoint to add a local channel that was not learned through gossip. This - * is the case for private channels or channels that have not yet reached - * `announce_depth`. - */ -bool routing_add_private_channel(struct routing_state *rstate, - const struct node_id *id, - struct amount_sat sat, - const u8 *chan_ann, u64 index); - /** * Get the local time. * diff --git a/gossipd/seeker.c b/gossipd/seeker.c index d0b2cb29d1b8..77500b34afa9 100644 --- a/gossipd/seeker.c +++ b/gossipd/seeker.c @@ -453,10 +453,6 @@ static bool get_unannounced_nodes(const tal_t *ctx, for (struct chan *c = uintmap_first(&rstate->chanmap, &offset); c; c = uintmap_after(&rstate->chanmap, &offset)) { - /* Local-only? Don't ask. */ - if (!is_chan_public(c)) - continue; - if (c->nodes[0]->bcast.index && c->nodes[1]->bcast.index) continue; diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index a299d0a35830..6f7c8595738a 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -67,9 +67,6 @@ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNE u32 timestamp UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, bool dying 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) -{ fprintf(stderr, "gossip_store_add_private_update called!\n"); abort(); } /* Generated stub for gossip_store_delete */ void gossip_store_delete(struct gossip_store *gs UNNEEDED, struct broadcastable *bcast UNNEEDED, @@ -80,11 +77,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 gossip_store_get_private_update */ -const u8 *gossip_store_get_private_update(const tal_t *ctx UNNEEDED, - struct gossip_store *gs UNNEEDED, - u64 offset UNNEEDED) -{ fprintf(stderr, "gossip_store_get_private_update called!\n"); abort(); } /* Generated stub for gossip_store_mark_channel_deleted */ void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, const struct short_channel_id *scid UNNEEDED) @@ -126,10 +118,6 @@ void peer_supplied_good_gossip(struct daemon *daemon UNNEEDED, const struct node_id *source_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 c5a024ccb082..a2635a50d63c 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -27,9 +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 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 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 *enable 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(); } @@ -72,9 +69,6 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, /* Generated stub for node_has_broadcastable_channels */ bool node_has_broadcastable_channels(const struct node *node UNNEEDED) { fprintf(stderr, "node_has_broadcastable_channels 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_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 2a16407d878a..28ba27f566f7 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -45,9 +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 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 first_random_peer */ struct peer *first_random_peer(struct daemon *daemon UNNEEDED, struct peer_node_id_map_iter *it UNNEEDED) diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 21c9290c8da8..750dc3ce1a6f 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -38,9 +38,6 @@ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNE u32 timestamp UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, bool dying 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) -{ fprintf(stderr, "gossip_store_add_private_update called!\n"); abort(); } /* Generated stub for gossip_store_delete */ void gossip_store_delete(struct gossip_store *gs UNNEEDED, struct broadcastable *bcast UNNEEDED, @@ -51,11 +48,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 gossip_store_get_private_update */ -const u8 *gossip_store_get_private_update(const tal_t *ctx UNNEEDED, - struct gossip_store *gs UNNEEDED, - u64 offset UNNEEDED) -{ fprintf(stderr, "gossip_store_get_private_update called!\n"); abort(); } /* Generated stub for gossip_store_mark_channel_deleted */ void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, const struct short_channel_id *scid UNNEEDED) @@ -92,10 +84,6 @@ void peer_supplied_good_gossip(struct daemon *daemon UNNEEDED, 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, From 9b3c4c564c2a76c2b1ec2d75fbb2da12b708e9c8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 18/38] common: remote private_channel_announcement.[ch]. Signed-off-by: Rusty Russell --- channeld/Makefile | 1 - common/Makefile | 1 - common/private_channel_announcement.c | 48 --------------------------- common/private_channel_announcement.h | 17 ---------- gossipd/Makefile | 1 - 5 files changed, 68 deletions(-) delete mode 100644 common/private_channel_announcement.c delete mode 100644 common/private_channel_announcement.h diff --git a/channeld/Makefile b/channeld/Makefile index 8bd27e42566f..940cd1128e09 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -83,7 +83,6 @@ CHANNELD_COMMON_OBJS := \ common/psbt_keypath.o \ common/psbt_open.o \ common/psbt_internal.o \ - common/private_channel_announcement.o \ common/pseudorand.o \ common/read_peer_msg.o \ common/setup.o \ diff --git a/common/Makefile b/common/Makefile index 55da9a41bae5..37529c6fcebc 100644 --- a/common/Makefile +++ b/common/Makefile @@ -77,7 +77,6 @@ COMMON_SRC_NOGEN := \ common/per_peer_state.c \ common/permute_tx.c \ common/ping.c \ - common/private_channel_announcement.c \ common/psbt_internal.c \ common/psbt_keypath.c \ common/psbt_open.c \ diff --git a/common/private_channel_announcement.c b/common/private_channel_announcement.c deleted file mode 100644 index 9247be3c7b28..000000000000 --- a/common/private_channel_announcement.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "config.h" -#include -#include -#include -#include - -const u8 *private_channel_announcement(const tal_t *ctx, - const struct short_channel_id *scid, - const struct node_id *local_node_id, - const struct node_id *remote_node_id, - const u8 *features) -{ - struct pubkey dummy_pubkey; - const struct node_id *node[2]; - struct secret not_a_secret; - - /* Make an all-zero sig. */ - static const u8 zeros[64]; - size_t zlen = sizeof(zeros); - const u8 *zerop = zeros; - secp256k1_ecdsa_signature zerosig; - fromwire_secp256k1_ecdsa_signature(&zerop, &zlen, &zerosig); - assert(zerop != NULL); - - memset(¬_a_secret, 1, sizeof(not_a_secret)); - if (!pubkey_from_secret(¬_a_secret, &dummy_pubkey)) - abort(); - - /* node ids are in ascending order. */ - if (node_id_cmp(remote_node_id, local_node_id) > 0) { - node[0] = local_node_id; - node[1] = remote_node_id; - } else { - node[0] = remote_node_id; - node[1] = local_node_id; - } - - return towire_channel_announcement(ctx, - &zerosig, &zerosig, - &zerosig, &zerosig, - features, - &chainparams->genesis_blockhash, - scid, - node[0], - node[1], - &dummy_pubkey, - &dummy_pubkey); -} diff --git a/common/private_channel_announcement.h b/common/private_channel_announcement.h deleted file mode 100644 index 45f98d3fc8bb..000000000000 --- a/common/private_channel_announcement.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef LIGHTNING_COMMON_PRIVATE_CHANNEL_ANNOUNCEMENT_H -#define LIGHTNING_COMMON_PRIVATE_CHANNEL_ANNOUNCEMENT_H -#include "config.h" -#include -#include - -struct short_channel_id; -struct node_id; - -/* Helper to create a fake channel announcement for local channels. */ -const u8 *private_channel_announcement(const tal_t *ctx, - const struct short_channel_id *scid, - const struct node_id *local_node_id, - const struct node_id *remote_node_id, - const u8 *features); - -#endif /* LIGHTNING_COMMON_PRIVATE_CHANNEL_ANNOUNCEMENT_H */ diff --git a/gossipd/Makefile b/gossipd/Makefile index c393de6a3950..5a1329864d7d 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -54,7 +54,6 @@ GOSSIPD_COMMON_OBJS := \ common/ping.o \ common/psbt_open.o \ common/pseudorand.o \ - common/private_channel_announcement.o \ common/random_select.o \ common/setup.o \ common/status.o \ From 4a3ae1527b77acccdf643c9079c6d9d16c21512b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 19/38] gossipd: remove channel update/annoounce creation and local update handling. Now lightningd just doesn't tell us about private channels, doesn't expect us to generate channel gossip, and tells us about public channels via the addgossip call, we don't need any of this in gossipd. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 544 --------------------- gossipd/gossip_generation.h | 16 - gossipd/gossipd.c | 168 +------ gossipd/gossipd_wire.csv | 32 -- gossipd/routing.c | 5 + gossipd/test/run-check_node_announcement.c | 27 - gossipd/test/run-crc32_of_update.c | 24 - lightningd/gossip_control.c | 7 - 8 files changed, 9 insertions(+), 814 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 619090e896df..c5022406d387 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -394,547 +394,3 @@ void maybe_send_own_node_announce(struct daemon *daemon, bool startup) update_own_node_announcement(daemon, startup, false); } -/* 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]); -} - -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]); -} - -static bool is_disabled(const u8 *channel_update) -{ - return *channel_flags_access(channel_update) & ROUTING_FLAGS_DISABLED; -} - -static bool is_enabled(const u8 *channel_update) -{ - return !is_disabled(channel_update); -} - - -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 (!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); - - /* Tell lightningd about this immediately (even if we're not actually - * applying it now). We choose not to send info about private - * channels, even in errors. */ - msg = towire_gossipd_got_local_channel_update(NULL, &chan->scid, - update); - daemon_conn_send(daemon->master, take(msg)); - - return update; -} - -static u8 *create_unsigned_update(const tal_t *ctx, - const struct short_channel_id *scid, - int direction, - bool enable, - u16 cltv_expiry_delta, - struct amount_msat htlc_minimum, - struct amount_msat htlc_maximum, - u32 fee_base_msat, - u32 fee_proportional_millionths, - bool public) -{ - secp256k1_ecdsa_signature dummy_sig; - u8 message_flags, channel_flags; - - /* So valgrind doesn't complain */ - memset(&dummy_sig, 0, sizeof(dummy_sig)); - - /* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #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. | - */ - channel_flags = direction; - if (!enable) - channel_flags |= ROUTING_FLAGS_DISABLED; - - /* BOLT #7: - * - * The `message_flags` bitfield is used to provide additional - * details about the message: - * - * | Bit Position | Name | - * | ------------- | ---------------| - * | 0 | `must_be_one` | - * | 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, - &dummy_sig, /* sig set later */ - &chainparams->genesis_blockhash, - scid, - 0, /* timestamp set later */ - message_flags, channel_flags, - cltv_expiry_delta, - htlc_minimum, - fee_base_msat, - fee_proportional_millionths, - htlc_maximum); -} - -static void apply_update(struct daemon *daemon, - const struct chan *chan, - int direction, - u8 *update TAKES) -{ - u8 *msg; - - msg = handle_channel_update(daemon->rstate, update, NULL, NULL, true); - if (msg) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "%s: rejected local channel update %s: %s", - __func__, - /* Normally we must not touch something taken() - * but we're in deep trouble anyway, and - * handle_channel_update only tal_steals onto - * tmpctx, so it's actually OK. */ - tal_hex(tmpctx, update), - tal_hex(tmpctx, msg)); -} - -static void sign_timestamp_and_apply_update(struct daemon *daemon, - const struct chan *chan, - int direction, - u8 *update TAKES) -{ - update = sign_and_timestamp_update(NULL, daemon, chan, direction, - update); - apply_update(daemon, chan, direction, take(update)); -} - -/* We don't want to thrash the gossip network, so we often defer sending an - * update. We track them here. */ -struct deferred_update { - /* 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; - /* Timer which will fire when it's time to apply. */ - struct oneshot *channel_update_timer; - /* The actual `update_channel` to apply */ - u8 *update; -}; - -static struct deferred_update *find_deferred_update(struct daemon *daemon, - const struct chan *chan) -{ - struct deferred_update *du; - - 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. */ -static 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; - - if (!is_halfchan_defined(&chan->half[direction])) - return NULL; - - prev = cast_const(u8 *, - gossip_store_get(tmpctx, daemon->rstate->gs, - chan->half[direction].bcast.index)); - - 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(prev, - &signature, &chain_hash, - &short_channel_id, ×tamp, - &message_flags, &channel_flags, - &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)); - return; - } - - /* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #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, - !(message_flags & ROUTING_OPT_DONT_FORWARD)); - sign_timestamp_and_apply_update(daemon, chan, direction, take(update)); -} - -/* 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 enable; - 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; - bool public; - - if (!fromwire_gossipd_local_channel_update(msg, - &id, - &scid, - &enable, - &cltv_expiry_delta, - &htlc_minimum, - &fee_base_msat, - &fee_proportional_millionths, - &htlc_maximum, - &public)) { - 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(&id, "local_channel_update for unknown %s", - type_to_string(tmpctx, struct short_channel_id, - &scid)); - return; - } - - if (!local_direction(daemon->rstate, chan, &direction)) { - status_peer_broken(&id, "bad local_channel_update chan %s", - type_to_string(tmpctx, - struct short_channel_id, - &scid)); - return; - } - - unsigned_update = create_unsigned_update(tmpctx, &scid, direction, - enable, cltv_expiry_delta, - htlc_minimum, htlc_maximum, - fee_base_msat, - fee_proportional_millionths, - public); - - hc = &chan->half[direction]; - - /* Ignore duplicates. */ - if (is_halfchan_defined(hc) - && !cupdate_different(daemon->rstate->gs, hc, unsigned_update)) - return; - - /* Too early? Defer (don't worry if it's unannounced). */ - if (is_halfchan_defined(hc)) { - 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; - } - } - - sign_timestamp_and_apply_update(daemon, chan, direction, - take(unsigned_update)); -} - -/* 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)); -} - -/* 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; - } - - /* 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); -} - -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 b39cc158b47f..50d3bb527002 100644 --- a/gossipd/gossip_generation.h +++ b/gossipd/gossip_generation.h @@ -35,20 +35,4 @@ 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); -/* 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 chan *chan, int direction); - -/* 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); - #endif /* LIGHTNING_GOSSIPD_GOSSIP_GENERATION_H */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index fe4ad3617b72..7505aa02de1b 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -46,41 +46,6 @@ bool peer_node_id_eq(const struct peer *peer, const struct node_id *node_id) return node_id_eq(&peer->id, node_id); } -/*~ A channel consists of a `struct half_chan` for each direction, each of - * which has a `flags` word from the `channel_update`; bit 1 is - * ROUTING_FLAGS_DISABLED in the `channel_update`. But we also keep a local - * 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, 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)) { - 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)) { - int direction; - if (!local_direction(daemon->rstate, c, &direction)) - continue; - local_enable_chan(daemon, c, direction); - } -} - /*~ Destroy a peer, usually because the per-peer daemon has exited. * * Were you wondering why we call this "destroy_peer" and not "peer_destroy"? @@ -90,16 +55,10 @@ static void peer_enable_channels(struct daemon *daemon, const struct node *node) */ static void destroy_peer(struct peer *peer) { - struct node *node; - /* Remove it from the peers table */ - peer_node_id_map_del(peer->daemon->peers, peer);; - - /* If we have a channel with this peer, disable it. */ - node = get_node(peer->daemon->rstate, &peer->id); - if (node) - peer_disable_channels(peer->daemon, node); + peer_node_id_map_del(peer->daemon->peers, peer); + /* Sorry seeker, this one is gone. */ seeker_peer_gone(peer->daemon->seeker, peer); } @@ -301,26 +260,6 @@ static u8 *handle_node_announce(struct peer *peer, const u8 *msg) return err; } -static void handle_local_channel_announcement(struct daemon *daemon, const u8 *msg) -{ - u8 *cannouncement; - const u8 *err; - struct node_id id; - - if (!fromwire_gossipd_local_channel_announcement(msg, msg, - &id, - &cannouncement)) - master_badmsg(WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT, msg); - - err = handle_channel_announcement_msg(daemon, &id, cannouncement); - if (err) { - status_peer_broken(&id, "invalid local_channel_announcement %s (%s)", - tal_hex(tmpctx, msg), - tal_hex(tmpctx, err)); - } -} - - /* lightningd tells us it has discovered and verified new `remote_addr`. * We can use this to update our node announcement. */ static void handle_discovered_ip(struct daemon *daemon, const u8 *msg) @@ -433,7 +372,6 @@ static void dump_our_gossip(struct daemon *daemon, struct peer *peer) static void connectd_new_peer(struct daemon *daemon, const u8 *msg) { struct peer *peer = tal(daemon, struct peer); - struct node *node; if (!fromwire_gossipd_new_peer(msg, &peer->id, &peer->gossip_queries_feature)) { @@ -463,10 +401,6 @@ static void connectd_new_peer(struct daemon *daemon, const u8 *msg) peer_node_id_map_add(daemon->peers, peer); tal_add_destructor(peer, destroy_peer); - node = get_node(daemon->rstate, &peer->id); - if (node) - peer_enable_channels(daemon, node); - /* Send everything we know about our own channels */ dump_our_gossip(daemon, peer); @@ -659,58 +593,13 @@ static struct io_plan *connectd_req(struct io_conn *conn, */ static void gossip_refresh_network(struct daemon *daemon) { - u64 now = gossip_time_now(daemon->rstate).ts.tv_sec; - s64 highwater; - struct node *n; - - /* Send out 1 day before deadline */ - highwater = now - (GOSSIP_PRUNE_INTERVAL(daemon->rstate->dev_fast_gossip) - - GOSSIP_BEFORE_DEADLINE(daemon->rstate->dev_fast_gossip_prune)); - /* Schedule next run now */ notleak(new_reltimer(&daemon->timers, daemon, time_from_sec(GOSSIP_PRUNE_INTERVAL(daemon->rstate->dev_fast_gossip_prune)/4), gossip_refresh_network, daemon)); - /* Find myself in the network */ - n = get_node(daemon->rstate, &daemon->id); - if (n) { - /* Iterate through all outgoing connection and check whether - * it's time to re-announce */ - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(n, &i); c; c = next_chan(n, &i)) { - struct half_chan *hc; - int direction; - - if (!local_direction(daemon->rstate, c, &direction)) - continue; - - hc = &c->half[direction]; - - if (!is_halfchan_defined(hc)) { - /* Connection is not announced yet, so don't even - * try to re-announce it */ - continue; - } - - if (hc->bcast.timestamp > highwater) { - /* No need to send a keepalive update message */ - continue; - } - - 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); - } - } - - /* Now we've refreshed our channels, we can prune without clobbering - * them */ + /* Prune: I hope lightningd is keeping up with our own channel + * refreshes! */ route_prune(daemon->rstate); } @@ -1013,38 +902,6 @@ static void handle_outpoints_spent(struct daemon *daemon, const u8 *msg) } } -/*~ This is sent by lightningd when it kicks off 'closingd': we disable it - * in both directions. - * - * We'll leave it to handle_outpoint_spent to delete the channel from our view - * once the close gets confirmed. This avoids having strange states in which the - * channel is list in our peer list but won't be returned when listing public - * channels. This does not send out updates since that's triggered by the peer - * connection closing. - */ -static void handle_local_channel_close(struct daemon *daemon, const u8 *msg) -{ - struct short_channel_id scid; - struct chan *chan; - struct routing_state *rstate = daemon->rstate; - if (!fromwire_gossipd_local_channel_close(msg, &scid)) - master_badmsg(WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE, msg); - - chan = get_channel(rstate, &scid); - 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); - } - } -} - /*~ This routine handles all the commands from lightningd. */ static struct io_plan *recv_req(struct io_conn *conn, const u8 *msg, @@ -1065,10 +922,6 @@ static struct io_plan *recv_req(struct io_conn *conn, handle_outpoints_spent(daemon, msg); goto done; - case WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE: - handle_local_channel_close(daemon, msg); - goto done; - case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: new_blockheight(daemon, msg); goto done; @@ -1084,18 +937,6 @@ 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; - - 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_DISCOVERED_IP: handle_discovered_ip(daemon, msg); goto done; @@ -1133,7 +974,6 @@ 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: case WIRE_GOSSIPD_REMOTE_CHANNEL_UPDATE: break; } diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 7a72606cb7ca..1f6dfdc8efc9 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -34,10 +34,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, -# gossipd->master: we're closing this channel. -msgtype,gossipd_local_channel_close,3027 -msgdata,gossipd_local_channel_close,short_channel_id,short_channel_id, - # Gossipd->master get this tx output please. msgtype,gossipd_get_txout,3018 msgdata,gossipd_get_txout,short_channel_id,short_channel_id, @@ -96,34 +92,6 @@ 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 - -# 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,enable,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, -msgdata,gossipd_local_channel_update,public,bool, - -# 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 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 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/gossipd/routing.c b/gossipd/routing.c index 1ad4905d0270..548c63bc6f78 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -2022,6 +2022,11 @@ void route_prune(struct routing_state *rstate) || chan->half[0].bcast.timestamp < highwater || !is_halfchan_defined(&chan->half[1]) || chan->half[1].bcast.timestamp < highwater) { + if (local_direction(rstate, chan, NULL)) + status_unusual("Pruning local channel %s from gossip_store: not refreshed in over two weeks", + type_to_string(tmpctx, struct short_channel_id, + &chan->scid)); + status_debug( "Pruning channel %s from network view (ages %"PRIu64" and %"PRIu64"s)", type_to_string(tmpctx, struct short_channel_id, diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index a2635a50d63c..e4f61d5ab08d 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -24,18 +24,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_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 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 *enable 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) -{ 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(); } /* 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(); } @@ -46,20 +34,11 @@ struct node *get_node(struct routing_state *rstate UNNEEDED, /* 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_update */ -u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *update TAKES UNNEEDED, - const struct node_id *source_peer TAKES UNNEEDED, - struct short_channel_id *unknown_scid UNNEEDED, - bool force UNNEEDED) -{ fprintf(stderr, "handle_channel_update called!\n"); abort(); } /* Generated stub for handle_node_announcement */ u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node_ann UNNEEDED, const struct node_id *source_peer TAKES UNNEEDED, bool *was_unknown UNNEEDED) { fprintf(stderr, "handle_node_announcement 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_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, @@ -79,12 +58,6 @@ 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(); } /* 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(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 28ba27f566f7..1516d5da6438 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -28,9 +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_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(); } @@ -52,15 +49,6 @@ struct peer *first_random_peer(struct daemon *daemon UNNEEDED, /* 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_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 *enable 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) -{ 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(); } /* 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(); } @@ -76,12 +64,6 @@ const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, /* 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_update */ -u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *update TAKES UNNEEDED, - const struct node_id *source_peer TAKES UNNEEDED, - struct short_channel_id *unknown_scid UNNEEDED, - bool force UNNEEDED) -{ fprintf(stderr, "handle_channel_update called!\n"); abort(); } /* Generated stub for handle_node_announcement */ u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node_ann UNNEEDED, const struct node_id *source_peer TAKES UNNEEDED, @@ -126,12 +108,6 @@ 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(); } /* 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(); } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 36933141e0b0..169de1a08e2f 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -161,16 +161,12 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) 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: case WIRE_GOSSIPD_DEV_MEMLEAK: case WIRE_GOSSIPD_DEV_COMPACT_STORE: case WIRE_GOSSIPD_DEV_SET_TIME: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: case WIRE_GOSSIPD_ADDGOSSIP: case WIRE_GOSSIPD_GET_ADDRS: - case WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT: - case WIRE_GOSSIPD_USED_LOCAL_CHANNEL_UPDATE: - case WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: @@ -184,9 +180,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_INIT_CUPDATE: handle_init_cupdate(gossip->ld, msg); break; - /* FIXME: remove from gossipd_wire.csv */ - case WIRE_GOSSIPD_GOT_LOCAL_CHANNEL_UPDATE: - break; case WIRE_GOSSIPD_GET_TXOUT: get_txout(gossip, msg); break; From b9cbf963a725dcf12245a10ad0efe93d7d80ef00 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 20/38] lightningd: check channel_announcement signatures we get from peer. We hoise check_signed_hash_nodeid from gossipd's internals, into common/. Signed-off-by: Rusty Russell --- common/node_id.c | 11 +++++++++++ common/node_id.h | 16 +++++++++++++++ gossipd/routing.c | 11 ----------- lightningd/channel_gossip.c | 22 +++++++++++++++++---- lightningd/gossip_generation.c | 36 +++++++++++++++++++++++++++++++++- lightningd/gossip_generation.h | 16 +++++++++++++++ wallet/test/run-wallet.c | 7 +++++++ 7 files changed, 103 insertions(+), 16 deletions(-) diff --git a/common/node_id.c b/common/node_id.c index 34cda6d54429..902b651b6923 100644 --- a/common/node_id.c +++ b/common/node_id.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -66,3 +67,13 @@ void towire_node_id(u8 **pptr, const struct node_id *id) #endif towire(pptr, id->k, sizeof(id->k)); } + +bool check_signed_hash_nodeid(const struct sha256_double *hash, + const secp256k1_ecdsa_signature *signature, + const struct node_id *id) +{ + struct pubkey key; + + return pubkey_from_node_id(&key, id) + && check_signed_hash(hash, signature, &key); +} diff --git a/common/node_id.h b/common/node_id.h index 1e2f8d0633c1..110c590ddecf 100644 --- a/common/node_id.h +++ b/common/node_id.h @@ -7,6 +7,8 @@ #include #include +struct sha256_double; + struct node_id { u8 k[PUBKEY_CMPR_LEN]; }; @@ -63,4 +65,18 @@ static inline size_t node_id_hash(const struct node_id *id) { return siphash24(siphash_seed(), id->k, sizeof(id->k)); } + +/** + * check_signed_hash_nodeid - check a raw secp256k1 signature. + * @h: hash which was signed. + * @signature: signature. + * @id: node_id corresponding to private key used to sign. + * + * Returns true if the id, hash and signature are correct. Changing any + * one of these will make it fail. + */ +bool check_signed_hash_nodeid(const struct sha256_double *hash, + const secp256k1_ecdsa_signature *signature, + const struct node_id *id); + #endif /* LIGHTNING_COMMON_NODE_ID_H */ diff --git a/gossipd/routing.c b/gossipd/routing.c index 548c63bc6f78..1e543a7d7b99 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -618,17 +618,6 @@ struct chan *new_chan(struct routing_state *rstate, return chan; } -/* Checks that key is valid, and signed this hash */ -static bool check_signed_hash_nodeid(const struct sha256_double *hash, - const secp256k1_ecdsa_signature *signature, - const struct node_id *id) -{ - struct pubkey key; - - return pubkey_from_node_id(&key, id) - && check_signed_hash(hash, signature, &key); -} - /* Verify the signature of a channel_update message */ static u8 *check_channel_update(const tal_t *ctx, const struct node_id *node_id, diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 6ba99ecd303c..afc985cc4c55 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -351,6 +351,21 @@ static void stash_remote_announce_sigs(struct channel *channel, const secp256k1_ecdsa_signature *bitcoin_sig) { struct channel_gossip *cg = channel->channel_gossip; + const char *err; + + /* BOLT #7: + * - if the `node_signature` OR the `bitcoin_signature` is NOT correct: + * - MAY send a `warning` and close the connection, or send an + * `error` and fail the channel. + */ + err = check_announce_sigs(channel, scid, node_sig, bitcoin_sig); + if (err) { + channel_fail_transient(channel, true, + "Bad gossip announcement_signatures for scid %s: %s", + short_channel_id_to_str(tmpctx, &scid), + err); + return; + } tal_free(cg->remote_sigs); cg->remote_sigs = tal(cg, struct remote_announce_sigs); @@ -375,7 +390,6 @@ static bool apply_remote_sigs(struct channel *channel) return false; } - /* FIXME: check sigs are valid! */ wallet_announcement_save(channel->peer->ld->wallet, channel->dbid, &cg->remote_sigs->node_sig, @@ -397,7 +411,7 @@ static void send_channel_announce_sigs(struct channel *channel) if (!channel_state_can_add_htlc(channel->state)) return; - ca = create_channel_announcement(tmpctx, channel, + ca = create_channel_announcement(tmpctx, channel, *channel->scid, NULL, NULL, NULL, NULL); msg = hsm_sync_req(tmpctx, ld, @@ -453,7 +467,7 @@ static void send_channel_announcement(struct channel *channel) const u8 *ca, *msg; struct channel_gossip *cg = channel->channel_gossip; - ca = create_channel_announcement(tmpctx, channel, + ca = create_channel_announcement(tmpctx, channel, *channel->scid, NULL, NULL, &cg->remote_sigs->node_sig, &cg->remote_sigs->bitcoin_sig); @@ -471,7 +485,7 @@ static void send_channel_announcement(struct channel *channel) if (!ld->gossip) return; - ca = create_channel_announcement(tmpctx, channel, + ca = create_channel_announcement(tmpctx, channel, *channel->scid, &local_node_sig, &local_bitcoin_sig, &cg->remote_sigs->node_sig, diff --git a/lightningd/gossip_generation.c b/lightningd/gossip_generation.c index ef9aee72a67c..1ec4700fce80 100644 --- a/lightningd/gossip_generation.c +++ b/lightningd/gossip_generation.c @@ -51,6 +51,7 @@ static void copysig_or_zero(secp256k1_ecdsa_signature *dst, u8 *create_channel_announcement(const tal_t *ctx, const struct channel *channel, + struct short_channel_id scid, const secp256k1_ecdsa_signature *local_node_signature, const secp256k1_ecdsa_signature *local_bitcoin_signature, const secp256k1_ecdsa_signature *remote_node_signature, @@ -72,7 +73,7 @@ u8 *create_channel_announcement(const tal_t *ctx, node_id[REMOTE] = channel->peer->id; funding_pubkey[LOCAL] = channel->local_funding_pubkey; funding_pubkey[REMOTE] = channel->channel_info.remote_fundingkey; - return create_channel_announcement_dir(ctx, features, *channel->scid, + return create_channel_announcement_dir(ctx, features, scid, node_signature, bitcoin_signature, node_id, funding_pubkey); } @@ -205,3 +206,36 @@ bool channel_update_details(const u8 *channel_update, *enabled = !(channel_flags & ROUTING_FLAGS_DISABLED); return true; } + +const char *check_announce_sigs(const struct channel *channel, + struct short_channel_id scid, + const secp256k1_ecdsa_signature *remote_node_signature, + const secp256k1_ecdsa_signature *remote_bitcoin_signature) +{ + struct sha256_double hash; + const u8 *cannounce; + + cannounce = create_channel_announcement(tmpctx, channel, scid, + NULL, NULL, NULL, NULL); + + /* BOLT #7: + * + * - MUST compute the double-SHA256 hash `h` of the message, beginning + * at offset 256, up to the end of the message. + * - Note: the hash skips the 4 signatures but hashes the rest of the + * message, including any future fields appended to the end. + */ + /* First two bytes are the msg type */ + int offset = 258; + sha256_double(&hash, cannounce + offset, tal_count(cannounce) - offset); + + if (!check_signed_hash_nodeid(&hash, remote_node_signature, + &channel->peer->id)) + return "invalid node_signature"; + + if (!check_signed_hash(&hash, remote_bitcoin_signature, + &channel->channel_info.remote_fundingkey)) + return "invalid bitcoin_signature"; + + return NULL; +} diff --git a/lightningd/gossip_generation.h b/lightningd/gossip_generation.h index 675143a8dae0..2208960a020a 100644 --- a/lightningd/gossip_generation.h +++ b/lightningd/gossip_generation.h @@ -10,6 +10,7 @@ struct channel; * create_channel_announcement: create a channel_announcement message * @ctx: the tal context to allocate return from * @channel: the channel to announce + * @scid: channel->scid * @local_node_signature: optional local node signature * @local_bitcoin_signature: optional local node signature * @remote_node_signature: optional peer node signature @@ -20,6 +21,7 @@ struct channel; */ u8 *create_channel_announcement(const tal_t *ctx, const struct channel *channel, + struct short_channel_id scid, const secp256k1_ecdsa_signature *local_node_signature, const secp256k1_ecdsa_signature *local_bitcoin_signature, const secp256k1_ecdsa_signature *remote_node_signature, @@ -61,4 +63,18 @@ bool channel_update_details(const u8 *channel_update, u32 *timestamp, bool *enabled); +/** + * check_announce_sigs: check that signatures are correct for this scid + * @channel: the channel (for peer's id / bitcoin key and the channel features) + * @scid: the short_channel_id it's proposing + * @remote_node_signature: node signature + * @remote_bitcoin_signature: bitcoin signature + * + * Returns a string literal if one signature is bad. + */ +const char *check_announce_sigs(const struct channel *channel, + struct short_channel_id scid, + const secp256k1_ecdsa_signature *remote_node_signature, + const secp256k1_ecdsa_signature *remote_bitcoin_signature); + #endif /* LIGHTNING_LIGHTNINGD_GOSSIP_GENERATION_H */ diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a3faf0285a8d..e7c30daae6d3 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -107,6 +107,12 @@ void channeld_tell_depth(struct channel *channel UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, u32 depth UNNEEDED) { fprintf(stderr, "channeld_tell_depth called!\n"); abort(); } +/* Generated stub for check_announce_sigs */ +const char *check_announce_sigs(const struct channel *channel UNNEEDED, + struct short_channel_id scid UNNEEDED, + const secp256k1_ecdsa_signature *remote_node_signature UNNEEDED, + const secp256k1_ecdsa_signature *remote_bitcoin_signature UNNEEDED) +{ fprintf(stderr, "check_announce_sigs called!\n"); abort(); } /* Generated stub for cmd_id_from_close_command */ const char *cmd_id_from_close_command(const tal_t *ctx UNNEEDED, struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED) @@ -174,6 +180,7 @@ struct anchor_details *create_anchor_details(const tal_t *ctx UNNEEDED, /* Generated stub for create_channel_announcement */ u8 *create_channel_announcement(const tal_t *ctx UNNEEDED, const struct channel *channel UNNEEDED, + struct short_channel_id scid UNNEEDED, const secp256k1_ecdsa_signature *local_node_signature UNNEEDED, const secp256k1_ecdsa_signature *local_bitcoin_signature UNNEEDED, const secp256k1_ecdsa_signature *remote_node_signature UNNEEDED, From 6d87999efe4ee36f1e6a56f621ebc3fa042fc21a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 21/38] lightningd: add routines to generate node_announcement. This is currently still done in gossipd, but we should generate it. Signed-off-by: Rusty Russell --- lightningd/channel_gossip.c | 65 +++++++++++++ lightningd/channel_gossip.h | 3 + lightningd/gossip_control.c | 5 + lightningd/gossip_generation.c | 166 +++++++++++++++++++++++++++++++++ lightningd/gossip_generation.h | 22 +++++ lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 + wallet/test/run-wallet.c | 13 +++ 8 files changed, 278 insertions(+) diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index afc985cc4c55..dfbd7f7d51de 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -982,3 +982,68 @@ const struct peer_update *channel_gossip_get_remote_update(const struct channel return NULL; return cg->peer_update; } + +static bool has_announced_channels(struct lightningd *ld) +{ + struct peer *peer; + struct peer_node_id_map_iter it; + + for (peer = peer_node_id_map_first(ld->peers, &it); + peer; + peer = peer_node_id_map_next(ld->peers, &it)) { + struct channel *channel; + list_for_each(&peer->channels, channel, list) { + /* Ignore unsaved channels */ + if (!channel->channel_gossip) + continue; + if (channel->channel_gossip->state == CGOSSIP_ANNOUNCED) + return true; + } + } + return false; +} + +static void node_announce_addgossip_reply(struct subd *gossipd, + const u8 *reply, + const int *fds UNUSED, + void *unused) +{ + char *err; + + if (!fromwire_gossipd_addgossip_reply(reply, reply, &err)) + fatal("Reading node_announce_addgossip_reply: %s", + tal_hex(tmpctx, reply)); + + if (strlen(err)) + log_broken(gossipd->ld->log, + "gossipd rejected our node announcement: %s", err); +} + +void channel_gossip_node_announce(struct lightningd *ld) +{ + u8 *nannounce; + const u8 *msg; + secp256k1_ecdsa_signature sig; + + /* Everyone will ignore our node_announcement unless we have + * announced a channel. */ + if (!has_announced_channels(ld)) + return; + + nannounce = unsigned_node_announcement(tmpctx, ld); + + /* Ask hsmd to sign it (synchronous) */ + msg = hsm_sync_req(tmpctx, ld, + take(towire_hsmd_node_announcement_sig_req(NULL, + nannounce))); + if (!fromwire_hsmd_node_announcement_sig_reply(msg, &sig)) + fatal("Reading hsmd_node_announcement_sig_reply: %s", + tal_hex(tmpctx, msg)); + + add_node_announcement_sig(nannounce, &sig); + + /* Tell gossipd. */ + subd_req(ld->gossip, ld->gossip, + take(towire_gossipd_addgossip(NULL, nannounce)), + -1, 0, node_announce_addgossip_reply, NULL); +} diff --git a/lightningd/channel_gossip.h b/lightningd/channel_gossip.h index 9d49df911d5c..e25d83d07543 100644 --- a/lightningd/channel_gossip.h +++ b/lightningd/channel_gossip.h @@ -29,6 +29,9 @@ void channel_gossip_got_announcement_sigs(struct channel *channel, const secp256k1_ecdsa_signature *node_sig, const secp256k1_ecdsa_signature *bitcoin_sig); +/* If node_announcement has changed, send a new one. */ +void channel_gossip_node_announce(struct lightningd *ld); + /* Gossipd told us about a channel update on one of our channels (on loading) */ void channel_gossip_update_from_gossipd(struct channel *channel, const u8 *channel_update TAKES); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 169de1a08e2f..6c19967561e5 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -336,6 +336,11 @@ static struct command_result *json_setleaserates(struct command *cmd, if (command_check_only(cmd)) return command_check_done(cmd); + /* Save them for node_announcement generation */ + cmd->ld->lease_rates = tal_free(cmd->ld->lease_rates); + if (!lease_rates_empty(rates)) + cmd->ld->lease_rates = tal_steal(cmd->ld, rates); + /* Call gossipd, let them know we've got new rates */ subd_send_msg(cmd->ld->gossip, take(towire_gossipd_new_lease_rates(NULL, rates))); diff --git a/lightningd/gossip_generation.c b/lightningd/gossip_generation.c index 1ec4700fce80..1d20bccc81f0 100644 --- a/lightningd/gossip_generation.c +++ b/lightningd/gossip_generation.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -239,3 +240,168 @@ const char *check_announce_sigs(const struct channel *channel, return NULL; } + +/* Get non-signature, non-timestamp parts of (valid!) node_announcement, + * with TLV broken out separately */ +static void get_nannounce_parts(const u8 *node_announcement, + const u8 *parts[3], + size_t sizes[3]) +{ + size_t len, ad_len; + const u8 *flen, *ad_start; + + /* BOLT #7: + * + * 1. type: 257 (`node_announcement`) + * 2. data: + * * [`signature`:`signature`] + * * [`u16`:`flen`] + * * [`flen*byte`:`features`] + * * [`u32`:`timestamp`] + *... + */ + /* Note: 2 bytes for `type` field */ + /* We already checked it's valid before accepting */ + assert(tal_count(node_announcement) > 2 + 64); + parts[0] = node_announcement + 2 + 64; + + /* Read flen to get size */ + flen = parts[0]; + len = tal_count(node_announcement) - (2 + 64); + sizes[0] = 2 + fromwire_u16(&flen, &len); + assert(flen != NULL && len >= 4); + + /* BOLT-0fe3485a5320efaa2be8cfa0e570ad4d0259cec3 #7: + * + * * [`u32`:`timestamp`] + * * [`point`:`node_id`] + * * [`3*byte`:`rgb_color`] + * * [`32*byte`:`alias`] + * * [`u16`:`addrlen`] + * * [`addrlen*byte`:`addresses`] + * * [`node_ann_tlvs`:`tlvs`] + */ + parts[1] = node_announcement + 2 + 64 + sizes[0] + 4; + + /* Find the end of the addresses */ + ad_start = parts[1] + 33 + 3 + 32; + len = tal_count(node_announcement) + - (2 + 64 + sizes[0] + 4 + 33 + 3 + 32); + ad_len = fromwire_u16(&ad_start, &len); + assert(ad_start != NULL && len >= ad_len); + + sizes[1] = 33 + 3 + 32 + 2 + ad_len; + + /* Is there a TLV ? */ + sizes[2] = len - ad_len; + if (sizes[2] != 0) + parts[2] = parts[1] + sizes[1]; + else + parts[2] = NULL; +} + +/* Is nann1 same as nann2 (not sigs and timestamps)? */ +bool node_announcement_same(const u8 *nann1, const u8 *nann2) +{ + const u8 *parts1[3], *parts2[3]; + size_t sizes1[3], sizes2[3]; + + get_nannounce_parts(nann1, parts1, sizes1); + get_nannounce_parts(nann2, parts2, sizes2); + + return memeq(parts1[0], sizes1[0], parts2[0], sizes2[0]) + && memeq(parts1[1], sizes1[1], parts2[1], sizes2[1]) + && memeq(parts1[2], sizes1[2], parts2[2], sizes2[2]); +} + +static u8 *create_nannounce(const tal_t *ctx, + struct lightningd *ld, + const secp256k1_ecdsa_signature *sig, + const struct wireaddr *addrs, + u32 timestamp, + const struct lease_rates *rates) +{ + u8 *addresses = tal_arr(tmpctx, u8, 0); + u8 *announcement; + struct tlv_node_ann_tlvs *na_tlv; + + for (size_t i = 0; i < tal_count(addrs); i++) + towire_wireaddr(&addresses, &addrs[i]); + + na_tlv = tlv_node_ann_tlvs_new(tmpctx); + na_tlv->option_will_fund = cast_const(struct lease_rates *, rates); + + announcement = + towire_node_announcement(ctx, sig, + ld->our_features->bits[NODE_ANNOUNCE_FEATURE], + timestamp, + &ld->id, ld->rgb, ld->alias, + addresses, + na_tlv); + return announcement; +} + +/* Return an array of wireaddr to announce */ +static const struct wireaddr *gather_addresses(const tal_t *ctx, + struct lightningd *ld) +{ + struct wireaddr *addrs; + + /* Note: If no announceable, tal_dup_talarr returns NULL! */ + addrs = tal_dup_talarr(ctx, struct wireaddr, ld->announceable); + if (!addrs) + addrs = tal_arr(ctx, struct wireaddr, 0); + + /* Add discovered IPs v4/v6 verified by peer `remote_addr` feature. */ + /* Only do that if we don't have any addresses announced or + * `config.ip_discovery` is explicitly enabled. */ + switch (ld->config.ip_discovery) { + case OPT_AUTOBOOL_FALSE: + return addrs; + case OPT_AUTOBOOL_TRUE: + break; + case OPT_AUTOBOOL_AUTO: + if (tal_count(addrs) != 0) + return addrs; + break; + } + + if (ld->discovered_ip_v4) + tal_arr_expand(&addrs, *ld->discovered_ip_v4); + if (ld->discovered_ip_v6) + tal_arr_expand(&addrs, *ld->discovered_ip_v6); + + return addrs; +} + +u8 *unsigned_node_announcement(const tal_t *ctx, + struct lightningd *ld) +{ + secp256k1_ecdsa_signature sig; + const struct wireaddr *addrs; + + addrs = gather_addresses(tmpctx, ld); + + memset(&sig, 0, sizeof(sig)); + return create_nannounce(tmpctx, ld, &sig, + addrs, time_now().ts.tv_sec, + ld->lease_rates); +} + +void add_node_announcement_sig(u8 *nannounce, + const secp256k1_ecdsa_signature *sig) +{ + u8 compact[64]; + + secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, compact, sig); + + /* BOLT #7: + * + * 1. type: 257 (`node_announcement`) + * 2. data: + * * [`signature`:`signature`] + */ + /* First two bytes are type */ + assert(tal_count(nannounce) > 2 + sizeof(compact)); + memcpy(nannounce + 2, compact, sizeof(compact)); +} diff --git a/lightningd/gossip_generation.h b/lightningd/gossip_generation.h index 2208960a020a..20769a11f9e4 100644 --- a/lightningd/gossip_generation.h +++ b/lightningd/gossip_generation.h @@ -77,4 +77,26 @@ const char *check_announce_sigs(const struct channel *channel, const secp256k1_ecdsa_signature *remote_node_signature, const secp256k1_ecdsa_signature *remote_bitcoin_signature); +/** + * unsigned_node_announcement: create a current unsigned node announcement. + * @ctx: the context to allocate return from + * @ld: the lightningd struct. + */ +u8 *unsigned_node_announcement(const tal_t *ctx, struct lightningd *ld); + +/** + * add_node_announcement_sig: apply the signature to the node announcement + * @nannounce: the (unsigned) node announcement + * @sig: the signature (from hsm) + */ +void add_node_announcement_sig(u8 *nannounce, + const secp256k1_ecdsa_signature *sig); + +/** + * node_announcement_same: are these two node_announcements the same? + * + * Ignoring timestamp and signatures. Basically, is it redundant? + */ +bool node_announcement_same(const u8 *nann1, const u8 *nann2); + #endif /* LIGHTNING_LIGHTNINGD_GOSSIP_GENERATION_H */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 778bf26ed6bc..ea1fc911e9c2 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -236,6 +236,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * so we disable this by default for now. */ ld->announce_dns = false; + ld->lease_rates = NULL; ld->remote_addr_v4 = NULL; ld->remote_addr_v6 = NULL; ld->discovered_ip_v4 = NULL; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index f823f59e7e1c..c0c1501ae4a0 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -187,6 +187,9 @@ struct lightningd { struct wireaddr_internal *binding; struct wireaddr *announceable; + /* Lease rates to advertize, set by json_setleaserates */ + struct lease_rates *lease_rates; + /* unverified remote_addr as reported by recent peers */ struct wireaddr *remote_addr_v4; struct wireaddr *remote_addr_v6; diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index e7c30daae6d3..00874ee3847f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -41,6 +41,10 @@ static void test_error(struct lightningd *ld, bool fatal, const char *fmt, va_li #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for add_node_announcement_sig */ +void add_node_announcement_sig(u8 *nannounce UNNEEDED, + const secp256k1_ecdsa_signature *sig UNNEEDED) +{ fprintf(stderr, "add_node_announcement_sig 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(); } @@ -311,6 +315,9 @@ bool fromwire_hsmd_init_reply_v4(const tal_t *ctx UNNEEDED, const void *p UNNEED /* 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_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_hsmd_sign_any_cannouncement_reply */ bool fromwire_hsmd_sign_any_cannouncement_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *node_signature UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature UNNEEDED) { fprintf(stderr, "fromwire_hsmd_sign_any_cannouncement_reply called!\n"); abort(); } @@ -1082,6 +1089,9 @@ u8 *towire_hsmd_init(const tal_t *ctx UNNEEDED, const struct bip32_key_version * /* 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_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_hsmd_sign_any_cannouncement_req */ u8 *towire_hsmd_sign_any_cannouncement_req(const tal_t *ctx UNNEEDED, const u8 *ca UNNEEDED, const struct node_id *peerid UNNEEDED, u64 channel_dbid UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_any_cannouncement_req called!\n"); abort(); } @@ -1160,6 +1170,9 @@ u8 *unsigned_channel_update(const tal_t *ctx UNNEEDED, bool forwardable UNNEEDED, bool enabled UNNEEDED) { fprintf(stderr, "unsigned_channel_update called!\n"); abort(); } +/* Generated stub for unsigned_node_announcement */ +u8 *unsigned_node_announcement(const tal_t *ctx UNNEEDED, struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "unsigned_node_announcement called!\n"); abort(); } /* Generated stub for unwrap_onionreply */ u8 *unwrap_onionreply(const tal_t *ctx UNNEEDED, const struct secret *shared_secrets UNNEEDED, From be6032e45a3a51c9eb6a6a1743c4ac783f328390 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 22/38] gossipd: tell lightningd its node_announcement on startup, if any. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 14 +++++++++++++- gossipd/gossipd_wire.csv | 5 +++++ lightningd/channel_gossip.c | 5 +++++ lightningd/gossip_control.c | 16 ++++++++++++++++ lightningd/lightningd.c | 1 + lightningd/lightningd.h | 5 ++++- 6 files changed, 44 insertions(+), 2 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 7505aa02de1b..5ffdaf0698ca 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -633,6 +633,17 @@ static void tell_master_local_cupdates(struct daemon *daemon) &c->scid, cupdate))); } + + /* Tell lightningd about our current node_announcement, if any */ + if (me->bcast.index) { + const u8 *nannounce; + nannounce = gossip_store_get(tmpctx, + daemon->rstate->gs, + me->bcast.index); + daemon_conn_send(daemon->master, + take(towire_gossipd_init_nannounce(NULL, + nannounce))); + } } struct peer *first_random_peer(struct daemon *daemon, @@ -721,7 +732,7 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) tal_add_destructor(daemon->connectd, master_or_connectd_gone); /* Tell it about all our local (public) channel_update messages, - * so it doesn't unnecessarily regenerate them. */ + * and node_announcement, so it doesn't unnecessarily regenerate them. */ tell_master_local_cupdates(daemon); /* OK, we are ready. */ @@ -967,6 +978,7 @@ static struct io_plan *recv_req(struct io_conn *conn, /* We send these, we don't receive them */ case WIRE_GOSSIPD_INIT_CUPDATE: + case WIRE_GOSSIPD_INIT_NANNOUNCE: case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 1f6dfdc8efc9..7b6b4fc82a75 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -24,6 +24,11 @@ msgdata,gossipd_init_cupdate,scid,short_channel_id, msgdata,gossipd_init_cupdate,len,u16, msgdata,gossipd_init_cupdate,cupdate,u8,len +# Gossipd tells us our node_announcement before init_reply. +msgtype,gossipd_init_nannounce,3102 +msgdata,gossipd_init_nannounce,len,u16, +msgdata,gossipd_init_nannounce,nannounce,u8,len + msgtype,gossipd_init_reply,3100 # In developer mode, we can mess with time. diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index dfbd7f7d51de..32c4a9f5e5b1 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -1032,6 +1032,11 @@ void channel_gossip_node_announce(struct lightningd *ld) nannounce = unsigned_node_announcement(tmpctx, ld); + /* Don't bother with duplicates */ + if (ld->node_announcement + && node_announcement_same(ld->node_announcement, nannounce)) + return; + /* Ask hsmd to sign it (synchronous) */ msg = hsm_sync_req(tmpctx, ld, take(towire_hsmd_node_announcement_sig_req(NULL, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 6c19967561e5..f0d065a0b38d 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -138,6 +138,19 @@ static void handle_init_cupdate(struct lightningd *ld, const u8 *msg) channel_gossip_update_from_gossipd(channel, take(update)); } +static void handle_init_nannounce(struct lightningd *ld, const u8 *msg) +{ + u8 *nannounce; + + if (!fromwire_gossipd_init_nannounce(ld, msg, &nannounce)) { + fatal("Gossip gave bad GOSSIPD_INIT_NANNOUNCE %s", + tal_hex(msg, msg)); + } + + assert(!ld->node_announcement); + ld->node_announcement = nannounce; +} + static void handle_peer_update_data(struct lightningd *ld, const u8 *msg) { struct peer_update update; @@ -180,6 +193,9 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_INIT_CUPDATE: handle_init_cupdate(gossip->ld, msg); break; + case WIRE_GOSSIPD_INIT_NANNOUNCE: + handle_init_nannounce(gossip->ld, msg); + break; case WIRE_GOSSIPD_GET_TXOUT: get_txout(gossip, msg); break; diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index ea1fc911e9c2..85ad052604e4 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -237,6 +237,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->announce_dns = false; ld->lease_rates = NULL; + ld->node_announcement = NULL; ld->remote_addr_v4 = NULL; ld->remote_addr_v6 = NULL; ld->discovered_ip_v4 = NULL; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index c0c1501ae4a0..4d4f37e90089 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -183,10 +183,13 @@ struct lightningd { /* Setup: And the bitset for each, whether to listen, announce or both */ enum addr_listen_announce *proposed_listen_announce; - /* Actual bindings and announceables from gossipd */ + /* Actual bindings and announceables from connectd */ struct wireaddr_internal *binding; struct wireaddr *announceable; + /* Current node announcement (if any) */ + const u8 *node_announcement; + /* Lease rates to advertize, set by json_setleaserates */ struct lease_rates *lease_rates; From 039de2b9a09a7afe804aaa3b767595c37454966c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:18 +1030 Subject: [PATCH 23/38] lightningd: don't create node_announcement until gossipd is finished. It might be redundant. Signed-off-by: Rusty Russell --- lightningd/channel_gossip.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 32c4a9f5e5b1..28b37ad120ec 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -53,7 +53,7 @@ struct channel_gossip { /* FIXME: this is a hack! We should wait 1 minute after connectd has * tried to connect to all peers. */ -static bool starting_up = true; +static bool starting_up = true, gossipd_init_done = false; /* We send non-forwardable channel updates if we can. */ static bool can_send_channel_update(const struct channel *channel) @@ -802,6 +802,7 @@ void channel_gossip_init_done(struct lightningd *ld) struct channel *channel; struct peer_node_id_map_iter it; + gossipd_init_done = true; for (peer = peer_node_id_map_first(ld->peers, &it); peer; peer = peer_node_id_map_next(ld->peers, &it)) { @@ -1030,6 +1031,11 @@ void channel_gossip_node_announce(struct lightningd *ld) if (!has_announced_channels(ld)) return; + /* Don't produce a node announcement until *after* gossipd has + * told us it's finished. */ + if (!gossipd_init_done) + return; + nannounce = unsigned_node_announcement(tmpctx, ld); /* Don't bother with duplicates */ From 33a1ba39408f68e331b5416af1a0a2a18676d6a2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 24/38] lightningd: refine heuristics for remote address selection. Rather than take the first two from peers with committed channels, use the most common address given by at least 2 peers, and accept the majority from non-committed peers if there are no committed peers. This works well with the node_announcement rework, which waits until everyone has a chance to connect before creating the node_announcement. Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 2 - lightningd/lightningd.h | 12 +-- lightningd/peer_control.c | 222 ++++++++++++++++++++++++++++---------- tests/test_connection.py | 17 +-- 4 files changed, 169 insertions(+), 84 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 85ad052604e4..b554393172e8 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -238,8 +238,6 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->lease_rates = NULL; ld->node_announcement = NULL; - ld->remote_addr_v4 = NULL; - ld->remote_addr_v6 = NULL; ld->discovered_ip_v4 = NULL; ld->discovered_ip_v6 = NULL; ld->listen = true; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 4d4f37e90089..79c0d20796dd 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -193,15 +193,9 @@ struct lightningd { /* Lease rates to advertize, set by json_setleaserates */ struct lease_rates *lease_rates; - /* 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; - - /* verified discovered IPs to be used for anouncement */ - struct wireaddr *discovered_ip_v4; - struct wireaddr *discovered_ip_v6; + /* Popular discovered IPs to be used for announcement */ + const struct wireaddr *discovered_ip_v4; + const struct wireaddr *discovered_ip_v6; /* Bearer of all my secrets. */ int hsm_fd; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index ee2a6a93a06d..b82449ef62a2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -1462,72 +1463,174 @@ 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) +/* Have they/we committed funds to the channel? */ +static bool channel_state_relationship(enum channel_state state) +{ + switch (state) { + case DUALOPEND_OPEN_INIT: + case DUALOPEND_OPEN_COMMIT_READY: + case DUALOPEND_OPEN_COMMITTED: + case DUALOPEND_AWAITING_LOCKIN: + case CHANNELD_AWAITING_LOCKIN: + return false; + case CLOSINGD_COMPLETE: + case AWAITING_UNILATERAL: + case FUNDING_SPEND_SEEN: + case ONCHAIN: + case CLOSED: + case CHANNELD_NORMAL: + case CHANNELD_AWAITING_SPLICE: + case CLOSINGD_SIGEXCHANGE: + case CHANNELD_SHUTTING_DOWN: + return true; + } + + abort(); +} + +/* We choose the most popular address we've given (if at least 2 give + * it), prefering peers which have a channel with us */ +struct discovered_addr { + bool preferred; + /* Port is uniformly set to our configured port in here. */ + struct wireaddr addr; +}; + +static int daddr_cmp(const struct discovered_addr *a, + const struct discovered_addr *b, + void *unused) +{ + return wireaddr_cmp_type(&a->addr, &b->addr, NULL); +} + +static const struct wireaddr *best_remote_addr(const tal_t *ctx, + struct lightningd *ld, + enum wire_addr_type atype) +{ + struct peer *peer; + struct peer_node_id_map_iter it; + struct discovered_addr *daddrs; + const struct wireaddr *best, *prev; + size_t best_score, cur_score, preferred_bonus; + + daddrs = tal_arr(tmpctx, struct discovered_addr, 0); + for (peer = peer_node_id_map_first(ld->peers, &it); + peer; + peer = peer_node_id_map_next(ld->peers, &it)) { + struct discovered_addr daddr; + if (!peer->remote_addr) + continue; + if (peer->remote_addr->type != atype) + continue; + daddr.preferred = peer_any_channel(peer, + channel_state_relationship, + NULL); + daddr.addr = *peer->remote_addr; + daddr.addr.port = ld->config.ip_discovery_port; + log_debug(ld->log, "best_remote_addr: peer %s gave addr %s (%s)", + type_to_string(tmpctx, struct node_id, &peer->id), + fmt_wireaddr(tmpctx, &daddr.addr), + daddr.preferred ? "preferred" : "no chan"); + tal_arr_expand(&daddrs, daddr); + } + + /* Sort into matching addresses */ + asort(daddrs, tal_count(daddrs), daddr_cmp, NULL); + + /* All the non-preferred peers cannot outvote 1 preferred peer */ + preferred_bonus = tal_count(daddrs); + best_score = cur_score = 0; + best = prev = NULL; + for (size_t i = 0; i < tal_count(daddrs); i++) { + if (prev && !wireaddr_eq(prev, &daddrs[i].addr)) { + if (cur_score > best_score) { + best_score = cur_score; + best = prev; + } + cur_score = 0; + } + cur_score += daddrs[i].preferred ? preferred_bonus : 1; + prev = &daddrs[i].addr; + } + if (cur_score > best_score) { + best_score = cur_score; + best = prev; + } + + if (!best) { + log_debug(ld->log, + "node_address: no peers gave remote addresses"); + return NULL; + } + + /* Does it agree with what we already know? */ + if (wireaddr_arr_contains(ld->announceable, best)) { + log_debug(ld->log, + "node_address: best address already being announced"); + return NULL; + } + + /* This means we got it from one preferred peer and at least one other */ + if (best_score > preferred_bonus) { + log_debug(ld->log, + "node_address: %zu peers gave remote addresses," + " best score %zu (preferred)", + tal_count(daddrs), best_score); + return tal_dup(ctx, struct wireaddr, best); + } + + /* No preferred peers gave us addresses? Use > 1 untrusted */ + if (best_score < preferred_bonus && best_score > 1) { + log_debug(ld->log, + "node_address: %zu peers gave remote addresses," + " best score %zu (no preferred)", + tal_count(daddrs), best_score); + return tal_dup(ctx, struct wireaddr, best); + } + + log_debug(ld->log, + "node_address: %zu peers gave remote addresses," + " best score %zu: not using", + tal_count(daddrs), best_score); + return NULL; +} + +/* Consider `remote_addr` from peer: if it could change things, reconsider + * what our discoverd IP is. Returns new address, or NULL. */ +static const struct wireaddr *update_remote_addr(struct lightningd *ld, + const struct wireaddr *remote_addr) { /* failsafe to prevent privacy leakage. */ if (ld->always_use_proxy || ld->config.ip_discovery == OPT_AUTOBOOL_FALSE) - return; + return NULL; 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 it's telling us what we already know, don't reevaluate */ + if (!ld->discovered_ip_v4 + || !wireaddr_eq(ld->discovered_ip_v4, remote_addr)) { + ld->discovered_ip_v4 = tal_free(ld->discovered_ip_v4); + ld->discovered_ip_v4 = best_remote_addr(ld, ld, ADDR_TYPE_IPV4); + return ld->discovered_ip_v4; } - /* 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; - } - /* tell gossip we have a valid update */ - 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 = ld->config.ip_discovery_port; - subd_send_msg(ld->gossip, towire_gossipd_discovered_ip( - tmpctx, - ld->discovered_ip_v4)); - } - /* store latest values */ - *ld->remote_addr_v4 = *remote_addr; - ld->remote_addr_v4_peer = peer_id; - break; + return NULL; 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 it's telling us what we already know, don't reevaluate */ + if (!ld->discovered_ip_v6 + || !wireaddr_eq(ld->discovered_ip_v6, remote_addr)) { + ld->discovered_ip_v6 = tal_free(ld->discovered_ip_v6); + ld->discovered_ip_v6 = best_remote_addr(ld, ld, ADDR_TYPE_IPV6); + return ld->discovered_ip_v6; } - if (node_id_eq(&ld->remote_addr_v6_peer, &peer_id)) { - *ld->remote_addr_v6 = *remote_addr; - break; - } - 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 = ld->config.ip_discovery_port; - subd_send_msg(ld->gossip, towire_gossipd_discovered_ip( - tmpctx, - ld->discovered_ip_v6)); - } - *ld->remote_addr_v6 = *remote_addr; - ld->remote_addr_v6_peer = peer_id; - break; - + return NULL; /* ignore all other cases */ case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_DNS: - break; + return NULL; } + abort(); } REGISTER_PLUGIN_HOOK(peer_connected, @@ -1590,9 +1693,7 @@ 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->remote_addr = tal_free(peer->remote_addr); hook_payload->peer_id = id; /* If there's a connect command, use its id as basis for hook id */ @@ -1600,14 +1701,19 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { + const struct wireaddr *best; 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 - * do stuff like probing for remote_addr to a random node. */ - if (!list_empty(&peer->channels)) - update_remote_addr(ld, hook_payload->remote_addr, id); + best = update_remote_addr(ld, peer->remote_addr); + if (best) { + log_debug(ld->log, + "Update our node_announcement for discovered address: %s", + fmt_wireaddr(tmpctx, best)); + subd_send_msg(ld->gossip, + towire_gossipd_discovered_ip(tmpctx, best)); + } } plugin_hook_call_peer_connected(ld, cmd_id, hook_payload); diff --git a/tests/test_connection.py b/tests/test_connection.py index 2646be97c960..02fa5c282189 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -125,26 +125,13 @@ def test_remote_addr(node_factory, bitcoind): 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. - 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:{}".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`. - l3.restart() + # connect second node. This will trigger `node_annoucement` update. 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:{}".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 + 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" From c7612a59202e2279cb77f19255a7e252e34104f5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 25/38] connectd: channel_gossip when we've tried to connect to all peers. It then waits 10 more seconds (for plugins to call setlease, especially) before it will update a node_announcement. Signed-off-by: Rusty Russell --- lightningd/channel_gossip.c | 14 ++++++++------ lightningd/channel_gossip.h | 3 +++ lightningd/connect_control.c | 12 ++++++++++++ lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 +++ lightningd/peer_control.c | 6 ++++++ lightningd/test/run-invoice-select-inchan.c | 3 +++ wallet/test/run-wallet.c | 3 +++ 8 files changed, 39 insertions(+), 6 deletions(-) diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 28b37ad120ec..1fd19836a164 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -51,8 +51,6 @@ struct channel_gossip { const struct peer_update *peer_update; }; -/* FIXME: this is a hack! We should wait 1 minute after connectd has - * tried to connect to all peers. */ static bool starting_up = true, gossipd_init_done = false; /* We send non-forwardable channel updates if we can. */ @@ -794,6 +792,14 @@ static void set_not_starting_up(struct lightningd *ld) log_debug(ld->log, "channel_gossip: no longer in startup mode"); } +/* We also wait ten seconds *after* connection, for lease registration */ +void channel_gossip_startup_done(struct lightningd *ld) +{ + notleak(new_reltimer(ld->timers, ld, + time_from_sec(10), + set_not_starting_up, ld)); +} + /* Gossipd init is done: if you expected a channel_update, be * disappointed. */ void channel_gossip_init_done(struct lightningd *ld) @@ -823,10 +829,6 @@ void channel_gossip_init_done(struct lightningd *ld) send_channel_announcement(channel); } } - - notleak(new_reltimer(ld->timers, ld, - time_from_sec(60), - set_not_starting_up, ld)); } /* Peer has connected. */ diff --git a/lightningd/channel_gossip.h b/lightningd/channel_gossip.h index e25d83d07543..0c2c79b40192 100644 --- a/lightningd/channel_gossip.h +++ b/lightningd/channel_gossip.h @@ -13,6 +13,9 @@ struct peer_update; void channel_gossip_init(struct channel *channel, const struct peer_update *remote_update TAKES); +/* We've attempted reconnect to all peers. */ +void channel_gossip_startup_done(struct lightningd *ld); + /* Something about channel/blockchain changed: update if required */ void channel_gossip_update(struct channel *channel); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 192f3de8f696..f13f42862469 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -565,6 +566,15 @@ void connectd_start_shutdown(struct subd *connectd) while (io_loop(NULL, NULL) != connectd); } +static void startup_connect_one_done(struct lightningd *ld) +{ + if (!ld->num_startup_connects) + return; + + if (--ld->num_startup_connects == 0) + channel_gossip_startup_done(ld); +} + static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fds) { enum connectd_wire t = fromwire_peektype(msg); @@ -594,6 +604,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd break; case WIRE_CONNECTD_PEER_CONNECTED: + startup_connect_one_done(connectd->ld); peer_connected(connectd->ld, msg); break; @@ -606,6 +617,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd break; case WIRE_CONNECTD_CONNECT_FAILED: + startup_connect_one_done(connectd->ld); handle_connect_failed(connectd->ld, msg); break; diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index b554393172e8..3913f364ea55 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -246,6 +246,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->try_reexec = false; ld->recover_secret = NULL; ld->db_upgrade_ok = NULL; + ld->num_startup_connects = 0; /* --experimental-upgrade-protocol */ ld->experimental_upgrade_protocol = false; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 79c0d20796dd..c344a4a0304c 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -172,6 +172,9 @@ struct lightningd { /* Do we want to reconnect to other peers? */ bool reconnect; + /* How many outstanding startup connection attempts? */ + size_t num_startup_connects; + /* Do we want to listen for other peers? */ bool listen; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index b82449ef62a2..109a8974b988 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2488,6 +2488,8 @@ static void setup_peer(struct peer *peer, u32 delay) /* Make sure connectd knows to try reconnecting. */ if (connect) { + ld->num_startup_connects++; + /* To delay, make it seem like we just connected. */ if (delay > 0) { peer->reconnect_delay = delay; @@ -2510,6 +2512,10 @@ void setup_peers(struct lightningd *ld) setup_peer(p, delay > 0 ? delay : 0); delay++; } + + /* In case there are no peers at all to connect to */ + if (ld->num_startup_connects == 0) + channel_gossip_startup_done(ld); } /* Pull peers, channels and HTLCs from db, and wire them up. */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 5e794c418b94..545a4294dfcd 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -90,6 +90,9 @@ const struct peer_update *channel_gossip_get_remote_update(const struct channel /* Generated stub for channel_gossip_peer_connected */ void channel_gossip_peer_connected(struct peer *peer UNNEEDED) { fprintf(stderr, "channel_gossip_peer_connected called!\n"); abort(); } +/* Generated stub for channel_gossip_startup_done */ +void channel_gossip_startup_done(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "channel_gossip_startup_done 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/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 00874ee3847f..a56c7b396f11 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -665,6 +665,9 @@ struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED) /* 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 node_announcement_same */ +bool node_announcement_same(const u8 *nann1 UNNEEDED, const u8 *nann2 UNNEEDED) +{ fprintf(stderr, "node_announcement_same 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(); } From 000fd7de6d2ae0332bf3f394c741ec5c18d91fb9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 26/38] lightningd: move node_announcement unit test from gossipd/ Signed-off-by: Rusty Russell --- gossipd/test/run-check_node_announcement.c | 125 ------------------ lightningd/test/run-check_node_announcement.c | 72 ++++++++++ 2 files changed, 72 insertions(+), 125 deletions(-) delete mode 100644 gossipd/test/run-check_node_announcement.c create mode 100644 lightningd/test/run-check_node_announcement.c diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c deleted file mode 100644 index e4f61d5ab08d..000000000000 --- a/gossipd/test/run-check_node_announcement.c +++ /dev/null @@ -1,125 +0,0 @@ -#include "config.h" -#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 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 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_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_node_announcement */ -u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node_ann UNNEEDED, - const struct node_id *source_peer TAKES UNNEEDED, - bool *was_unknown UNNEEDED) -{ fprintf(stderr, "handle_node_announcement 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 node_has_broadcastable_channels */ -bool node_has_broadcastable_channels(const struct node *node UNNEEDED) -{ fprintf(stderr, "node_has_broadcastable_channels 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 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 wire_sync_read */ -u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) -{ fprintf(stderr, "wire_sync_read 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 */ - -/* Overwriting this to return test data */ -const u8 *gossip_store_get(const tal_t *ctx, - struct gossip_store *gs, - u64 offset) -{ - /* No TLV, different */ - if (offset == 0) - return tal_hexdata(ctx, "01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000", strlen("01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000")); - - /* No TLV, same */ - if (offset == 1) - return tal_hexdata(ctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000")); - - /* Same, with TLV */ - if (offset == 2) - return tal_hexdata(ctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); - - return NULL; -} - -int main(int argc, char *argv[]) -{ - struct node node; - bool only_missing_tlv; - u8 *ann; - - common_setup(argv[0]); - - /* No TLV checks */ - node.bcast.index = 1; - ann = tal_hexdata(tmpctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000")); - assert(!nannounce_different(NULL, &node, ann, &only_missing_tlv)); - assert(!only_missing_tlv); - - node.bcast.index = 0; - assert(nannounce_different(NULL, &node, ann, &only_missing_tlv)); - assert(!only_missing_tlv); - - /* TLV checks */ - ann = tal_hexdata(tmpctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); - - node.bcast.index = 2; - assert(!nannounce_different(NULL, &node, ann, &only_missing_tlv)); - assert(!only_missing_tlv); - - /* Tweak the last, check that it fails */ - node.bcast.index = 2; - ann[tal_count(ann) - 1]++; - assert(nannounce_different(NULL, &node, ann, &only_missing_tlv)); - assert(only_missing_tlv); - - common_shutdown(); - return 0; -} diff --git a/lightningd/test/run-check_node_announcement.c b/lightningd/test/run-check_node_announcement.c new file mode 100644 index 000000000000..76935e22db2a --- /dev/null +++ b/lightningd/test/run-check_node_announcement.c @@ -0,0 +1,72 @@ +#include "config.h" +#include "../gossip_generation.c" +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for check_signed_hash_nodeid */ +bool check_signed_hash_nodeid(const struct sha256_double *hash UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED, + const struct node_id *id UNNEEDED) +{ fprintf(stderr, "check_signed_hash_nodeid 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 */ +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 */ +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 get_agreed_channelfeatures */ +u8 *get_agreed_channelfeatures(const tal_t *ctx UNNEEDED, + const struct feature_set *our_features UNNEEDED, + const u8 *theirfeatures UNNEEDED) +{ fprintf(stderr, "get_agreed_channelfeatures 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(); } +/* 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(); } +/* AUTOGENERATED MOCKS END */ + +int main(int argc, char *argv[]) +{ + u8 *ann, *ann2; + + common_setup(argv[0]); + + /* No TLV checks */ + ann = tal_hexdata(tmpctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000")); + ann2 = tal_hexdata(tmpctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000"));; + assert(node_announcement_same(ann, ann2)); + + ann2 = tal_hexdata(tmpctx, "01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000", strlen("01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000")); + assert(!node_announcement_same(ann, ann2)); + + /* TLV checks */ + ann = tal_hexdata(tmpctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); + ann2 = tal_hexdata(tmpctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); + + assert(node_announcement_same(ann, ann2)); + + /* Tweak the last, check that it fails */ + ann[tal_count(ann) - 1]++; + assert(!node_announcement_same(ann, ann2)); + + common_shutdown(); + return 0; +} From bbc2ecd78c06821dcc55f0afd5d6ee407b4a0917 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 27/38] lightningd: turn on node_announcement generation. Signed-off-by: Rusty Russell --- lightningd/channel_gossip.c | 8 ++++++++ lightningd/gossip_control.c | 5 ++--- lightningd/peer_control.c | 3 +-- lightningd/test/run-invoice-select-inchan.c | 6 +++--- wallet/test/run-wallet.c | 3 --- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 1fd19836a164..98f8d2cd42e9 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -494,6 +494,8 @@ static void send_channel_announcement(struct channel *channel) -1, 0, send_channel_announce_addgossip_reply, channel); /* We can also send our first public channel_update now */ broadcast_public_cupdate(channel, true); + /* And maybe our first node_announcement */ + channel_gossip_node_announce(ld); } static void set_gossip_state(struct channel *channel, @@ -790,6 +792,8 @@ static void set_not_starting_up(struct lightningd *ld) { starting_up = false; log_debug(ld->log, "channel_gossip: no longer in startup mode"); + /* Now we can create/update a node_announcement */ + channel_gossip_node_announce(ld); } /* We also wait ten seconds *after* connection, for lease registration */ @@ -1055,6 +1059,10 @@ void channel_gossip_node_announce(struct lightningd *ld) add_node_announcement_sig(nannounce, &sig); + /* Update our cached copy. */ + tal_free(ld->node_announcement); + ld->node_announcement = tal_steal(ld, nannounce); + /* Tell gossipd. */ subd_req(ld->gossip, ld->gossip, take(towire_gossipd_addgossip(NULL, nannounce)), diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index f0d065a0b38d..42bd13e46a19 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -357,9 +357,8 @@ static struct command_result *json_setleaserates(struct command *cmd, if (!lease_rates_empty(rates)) cmd->ld->lease_rates = tal_steal(cmd->ld, rates); - /* Call gossipd, let them know we've got new rates */ - subd_send_msg(cmd->ld->gossip, - take(towire_gossipd_new_lease_rates(NULL, rates))); + /* This may generate a new node_announcement */ + channel_gossip_node_announce(cmd->ld); res = json_stream_success(cmd); json_add_amount_sat_msat(res, "lease_fee_base_msat", diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 109a8974b988..82bf0333c4c1 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1711,8 +1711,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) log_debug(ld->log, "Update our node_announcement for discovered address: %s", fmt_wireaddr(tmpctx, best)); - subd_send_msg(ld->gossip, - towire_gossipd_discovered_ip(tmpctx, best)); + channel_gossip_node_announce(ld); } } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 545a4294dfcd..39ff65bbdb54 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -87,6 +87,9 @@ void channel_fail_transient(struct channel *channel UNNEEDED, /* Generated stub for channel_gossip_get_remote_update */ const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel UNNEEDED) { fprintf(stderr, "channel_gossip_get_remote_update called!\n"); abort(); } +/* Generated stub for channel_gossip_node_announce */ +void channel_gossip_node_announce(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "channel_gossip_node_announce called!\n"); abort(); } /* Generated stub for channel_gossip_peer_connected */ void channel_gossip_peer_connected(struct peer *peer UNNEEDED) { fprintf(stderr, "channel_gossip_peer_connected called!\n"); abort(); } @@ -951,9 +954,6 @@ 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_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_preapprove_invoice */ u8 *towire_hsmd_preapprove_invoice(const tal_t *ctx UNNEEDED, const wirestring *invstring UNNEEDED) { fprintf(stderr, "towire_hsmd_preapprove_invoice called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a56c7b396f11..84649549a8cd 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1065,9 +1065,6 @@ u8 *towire_final_incorrect_htlc_amount(const tal_t *ctx UNNEEDED, struct amount_ /* Generated stub for towire_gossipd_addgossip */ u8 *towire_gossipd_addgossip(const tal_t *ctx UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "towire_gossipd_addgossip 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_check_pubkey */ u8 *towire_hsmd_check_pubkey(const tal_t *ctx UNNEEDED, u32 index UNNEEDED, const struct pubkey *pubkey UNNEEDED) { fprintf(stderr, "towire_hsmd_check_pubkey called!\n"); abort(); } From 609d8853b5e1300da11e27602a221e5c3664c2d5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 28/38] gossipd: remove node_announcement generation. Signed-off-by: Rusty Russell --- gossipd/Makefile | 1 - gossipd/gossip_generation.c | 396 ------------------ gossipd/gossip_generation.h | 38 -- gossipd/gossipd.c | 99 +---- gossipd/gossipd.h | 24 -- gossipd/gossipd_wire.csv | 13 - gossipd/queries.c | 25 +- gossipd/queries.h | 5 + gossipd/routing.c | 106 ++++- gossipd/routing.h | 17 - gossipd/test/run-check_channel_announcement.c | 16 +- gossipd/test/run-crc32_of_update.c | 34 -- gossipd/test/run-extended-info.c | 5 - gossipd/test/run-txout_failure.c | 16 +- lightningd/gossip_control.c | 8 +- 15 files changed, 140 insertions(+), 663 deletions(-) delete mode 100644 gossipd/gossip_generation.c delete mode 100644 gossipd/gossip_generation.h diff --git a/gossipd/Makefile b/gossipd/Makefile index 5a1329864d7d..42e79f256963 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -6,7 +6,6 @@ GOSSIPD_HEADERS_WSRC := gossipd/gossipd_wiregen.h \ gossipd/gossipd.h \ gossipd/gossip_store.h \ gossipd/queries.h \ - gossipd/gossip_generation.h \ gossipd/routing.h \ gossipd/seeker.h GOSSIPD_HEADERS := $(GOSSIPD_HEADERS_WSRC) gossipd/broadcast.h diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c deleted file mode 100644 index c5022406d387..000000000000 --- a/gossipd/gossip_generation.c +++ /dev/null @@ -1,396 +0,0 @@ -/* Routines to make our own gossip messages. Not as in "we're the gossip - * generation, man!" */ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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 - * second time to build the full packet with the signature. The timestamp is - * handed in rather than using time_now() internally, since that could change - * between the dummy creation and the call with a signature. */ -static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, - const secp256k1_ecdsa_signature *sig, - 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, count_announceable; - - /* add all announceable addresses */ - count_announceable = tal_count(daemon->announceable); - was = tal_arr(tmpctx, struct wireaddr, 0); - for (i = 0; i < count_announceable; i++) - tal_arr_expand(&was, daemon->announceable[i]); - - /* Add discovered IPs v4/v6 verified by peer `remote_addr` feature. */ - /* Only do that if we don't have any addresses announced or - * `config.ip_discovery` is explicitly enabled. */ - if ((daemon->ip_discovery == OPT_AUTOBOOL_AUTO && count_announceable == 0) || - daemon->ip_discovery == OPT_AUTOBOOL_TRUE) { - 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 discovered_ip 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(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); - - announcement = - towire_node_announcement(ctx, sig, - daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], - timestamp, - &daemon->id, daemon->rgb, daemon->alias, - addresses, - na_tlv); - return announcement; -} - -/* Helper to get non-signature, non-timestamp parts of (valid!) channel_update */ -void get_cupdate_parts(const u8 *channel_update, - const u8 *parts[2], - size_t sizes[2]) -{ - /* BOLT #7: - * - * 1. type: 258 (`channel_update`) - * 2. data: - * * [`signature`:`signature`] - * * [`chain_hash`:`chain_hash`] - * * [`short_channel_id`:`short_channel_id`] - * * [`u32`:`timestamp`] - *... - */ - /* Note: 2 bytes for `type` field */ - /* We already checked it's valid before accepting */ - assert(tal_count(channel_update) > 2 + 64 + 32 + 8 + 4); - parts[0] = channel_update + 2 + 64; - sizes[0] = 32 + 8; - parts[1] = channel_update + 2 + 64 + 32 + 8 + 4; - sizes[1] = tal_count(channel_update) - (64 + 2 + 32 + 8 + 4); -} - -/* Is this channel_update different from prev (not sigs and timestamps)? */ -bool cupdate_different(struct gossip_store *gs, - const struct half_chan *hc, - const u8 *cupdate) -{ - const u8 *oparts[2], *nparts[2]; - size_t osizes[2], nsizes[2]; - const u8 *orig; - - /* Get last one we have. */ - orig = gossip_store_get(tmpctx, gs, hc->bcast.index); - get_cupdate_parts(orig, oparts, osizes); - get_cupdate_parts(cupdate, nparts, nsizes); - - return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) - || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]); -} - -/* Get non-signature, non-timestamp parts of (valid!) node_announcement, - * with TLV broken out separately */ -static void get_nannounce_parts(const u8 *node_announcement, - const u8 *parts[3], - size_t sizes[3]) -{ - size_t len, ad_len; - const u8 *flen, *ad_start; - - /* BOLT #7: - * - * 1. type: 257 (`node_announcement`) - * 2. data: - * * [`signature`:`signature`] - * * [`u16`:`flen`] - * * [`flen*byte`:`features`] - * * [`u32`:`timestamp`] - *... - */ - /* Note: 2 bytes for `type` field */ - /* We already checked it's valid before accepting */ - assert(tal_count(node_announcement) > 2 + 64); - parts[0] = node_announcement + 2 + 64; - - /* Read flen to get size */ - flen = parts[0]; - len = tal_count(node_announcement) - (2 + 64); - sizes[0] = 2 + fromwire_u16(&flen, &len); - assert(flen != NULL && len >= 4); - - /* BOLT-0fe3485a5320efaa2be8cfa0e570ad4d0259cec3 #7: - * - * * [`u32`:`timestamp`] - * * [`point`:`node_id`] - * * [`3*byte`:`rgb_color`] - * * [`32*byte`:`alias`] - * * [`u16`:`addrlen`] - * * [`addrlen*byte`:`addresses`] - * * [`node_ann_tlvs`:`tlvs`] - */ - parts[1] = node_announcement + 2 + 64 + sizes[0] + 4; - - /* Find the end of the addresses */ - ad_start = parts[1] + 33 + 3 + 32; - len = tal_count(node_announcement) - - (2 + 64 + sizes[0] + 4 + 33 + 3 + 32); - ad_len = fromwire_u16(&ad_start, &len); - assert(ad_start != NULL && len >= ad_len); - - sizes[1] = 33 + 3 + 32 + 2 + ad_len; - - /* Is there a TLV ? */ - sizes[2] = len - ad_len; - if (sizes[2] != 0) - parts[2] = parts[1] + sizes[1]; - else - parts[2] = NULL; -} - -/* Is this node_announcement different from prev (not sigs and timestamps)? */ -bool nannounce_different(struct gossip_store *gs, - const struct node *node, - const u8 *nannounce, - bool *only_missing_tlv) -{ - const u8 *oparts[3], *nparts[3]; - size_t osizes[3], nsizes[3]; - const u8 *orig; - - /* Get last one we have. */ - orig = gossip_store_get(tmpctx, gs, node->bcast.index); - get_nannounce_parts(orig, oparts, osizes); - get_nannounce_parts(nannounce, nparts, nsizes); - - if (only_missing_tlv) - *only_missing_tlv = memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) - && memeq(oparts[1], osizes[1], nparts[1], nsizes[1]) - && !memeq(oparts[2], osizes[2], nparts[2], nsizes[2]); - - return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) - || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]) - || !memeq(oparts[2], osizes[2], nparts[2], nsizes[2]); -} - -static void sign_and_send_nannounce(struct daemon *daemon, - u8 *nannounce, - u32 timestamp) -{ - secp256k1_ecdsa_signature sig; - u8 *msg, *err; - - /* Ask hsmd to sign it (synchronous) */ - if (!wire_sync_write(HSM_FD, take(towire_hsmd_node_announcement_sig_req(NULL, nannounce)))) - status_failed(STATUS_FAIL_MASTER_IO, "Could not write to HSM: %s", strerror(errno)); - - msg = wire_sync_read(tmpctx, HSM_FD); - if (!fromwire_hsmd_node_announcement_sig_reply(msg, &sig)) - status_failed(STATUS_FAIL_MASTER_IO, "HSM returned an invalid node_announcement sig"); - - /* We got the signature for our provisional node_announcement back - * from the HSM, create the real announcement and forward it to - * gossipd so it can take care of forwarding it. */ - nannounce = create_node_announcement(NULL, daemon, &sig, - timestamp, daemon->rates); - - /* This injects it into the routing code in routing.c; it should not - * reject it! */ - err = handle_node_announcement(daemon->rstate, take(nannounce), - NULL, NULL); - if (err) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "rejected own node announcement: %s", - tal_hex(tmpctx, err)); -} - - -/* 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. */ -static void update_own_node_announcement(struct daemon *daemon, - bool startup, - bool always_refresh) -{ - u32 timestamp = gossip_time_now(daemon->rstate).ts.tv_sec; - u8 *nannounce; - struct node *self = get_node(daemon->rstate, &daemon->id); - - /* If we don't have any channels now, don't send node_announcement */ - if (!self || !node_has_broadcastable_channels(self)) - goto reset_timer; - - /* If we ever use set-based propagation, ensuring the toggle the lower - * bit in consecutive timestamps makes it more robust. */ - if (self && self->bcast.index - && (timestamp & 1) == (self->bcast.timestamp & 1)) - timestamp++; - - /* Make unsigned announcement. */ - nannounce = create_node_announcement(tmpctx, daemon, NULL, - timestamp, - daemon->rates); - - /* If it's the same as the previous, nothing to do. */ - if (self && self->bcast.index) { - u32 next; - bool only_missing_tlv; - - if (!nannounce_different(daemon->rstate->gs, self, nannounce, - &only_missing_tlv)) { - if (always_refresh) - goto send; - /* Update if old announcement is at least 7 days old. */ - if (timestamp > self->bcast.timestamp && - timestamp - self->bcast.timestamp > - GOSSIP_PRUNE_INTERVAL(daemon->rstate->dev_fast_gossip_prune) / 2) - goto send; - /* First time? Start regen timer. */ - if (!daemon->node_announce_regen_timer) - goto reset_timer; - return; - } - - /* Missing liquidity_ad, maybe we'll get plugin callback */ - if (startup && only_missing_tlv) { - u32 delay = GOSSIP_NANN_STARTUP_DELAY(daemon->rstate->dev_fast_gossip); - status_debug("node_announcement: delaying" - " %u secs at start", delay); - - /* Discard existing timer. */ - daemon->node_announce_timer - = tal_free(daemon->node_announce_timer); - daemon->node_announce_timer - = new_reltimer(&daemon->timers, - daemon, - time_from_sec(delay), - update_own_node_announcement_after_startup, - daemon); - return; - } - /* BOLT #7: - * - * The origin node: - * - MUST set `timestamp` to be greater than that of any - * previous `node_announcement` it has previously created. - */ - /* We do better: never send them within more than 5 minutes. */ - next = self->bcast.timestamp - + GOSSIP_MIN_INTERVAL(daemon->rstate->dev_fast_gossip); - - if (timestamp < next) { - status_debug("node_announcement: delaying %u secs", - next - timestamp); - - /* Discard existing timer. */ - daemon->node_announce_timer - = tal_free(daemon->node_announce_timer); - daemon->node_announce_timer - = new_reltimer(&daemon->timers, - daemon, - time_from_sec(next - timestamp), - update_own_node_announcement_after_startup, - daemon); - return; - } - } - -send: - sign_and_send_nannounce(daemon, nannounce, timestamp); - -reset_timer: - /* Generate another one in 24 hours. */ - setup_force_nannounce_regen_timer(daemon); - - return; -} - -static void update_own_node_announcement_after_startup(struct daemon *daemon) -{ - update_own_node_announcement(daemon, false, false); -} - -/* This creates and transmits a *new* node announcement */ -static void force_self_nannounce_regen(struct daemon *daemon) -{ - update_own_node_announcement(daemon, false, true); -} - -/* Because node_announcement propagation is spotty, we regenerate this every - * 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 (daemon->rstate->dev_fast_gossip_prune) - 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, - regen_time, - 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) -{ - /* We keep an internal flag in the routing code to say we've announced - * a local channel. The alternative would be to have it make a - * callback, but when we start up we don't want to make multiple - * announcments, so we use this approach for now. */ - if (!daemon->rstate->local_channel_announced) - return; - - update_own_node_announcement(daemon, startup, false); -} - diff --git a/gossipd/gossip_generation.h b/gossipd/gossip_generation.h deleted file mode 100644 index 50d3bb527002..000000000000 --- a/gossipd/gossip_generation.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef LIGHTNING_GOSSIPD_GOSSIP_GENERATION_H -#define LIGHTNING_GOSSIPD_GOSSIP_GENERATION_H -#include "config.h" -#include -#include -#include - -struct chan; -struct daemon; -struct half_chan; -struct local_chan; -struct gossip_store; -struct peer; -struct node; - -/* Helper to get non-signature, non-timestamp parts of (valid!) channel_update */ -void get_cupdate_parts(const u8 *channel_update, - const u8 *parts[2], - size_t sizes[2]); - - -/* Is this channel_update different from prev (not sigs and timestamps)? - * is_halfchan_defined(hc) must be true! */ -bool cupdate_different(struct gossip_store *gs, - const struct half_chan *hc, - const u8 *cupdate); - -/* Is this node_announcement different from prev (not sigs and timestamps)? - * node->bcast.index must be non-zero! */ -bool nannounce_different(struct gossip_store *gs, - const struct node *node, - const u8 *nannounce, - bool *only_missing_tlv); - -/* Should we announce our own node? Called at strategic places. */ -void maybe_send_own_node_announce(struct daemon *daemon, bool startup); - -#endif /* LIGHTNING_GOSSIPD_GOSSIP_GENERATION_H */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 5ffdaf0698ca..b20118c98e46 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -239,12 +238,6 @@ static u8 *handle_channel_update_msg(struct peer *peer, const u8 *msg) 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 - * routing until you have both anyway. For this reason, we might have - * just sent out our own channel_announce, so we check if it's time to - * send a node_announcement too. */ - maybe_send_own_node_announce(peer->daemon, false); return NULL; } @@ -260,53 +253,6 @@ static u8 *handle_node_announce(struct peer *peer, const u8 *msg) return err; } -/* lightningd tells us it has discovered and verified new `remote_addr`. - * We can use this to update our node announcement. */ -static void handle_discovered_ip(struct daemon *daemon, const u8 *msg) -{ - struct wireaddr discovered_ip; - size_t count_announceable; - - if (!fromwire_gossipd_discovered_ip(msg, &discovered_ip)) - master_badmsg(WIRE_GOSSIPD_DISCOVERED_IP, msg); - - switch (discovered_ip.type) { - case ADDR_TYPE_IPV4: - if (daemon->discovered_ip_v4 != NULL && - wireaddr_eq_without_port(daemon->discovered_ip_v4, - &discovered_ip)) - break; - 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->discovered_ip_v6 != NULL && - wireaddr_eq_without_port(daemon->discovered_ip_v6, - &discovered_ip)) - break; - 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 */ - case ADDR_TYPE_TOR_V2_REMOVED: - case ADDR_TYPE_TOR_V3: - case ADDR_TYPE_DNS: - break; - } - return; - -update_node_annoucement: - count_announceable = tal_count(daemon->announceable); - if ((daemon->ip_discovery == OPT_AUTOBOOL_AUTO && count_announceable == 0) || - daemon->ip_discovery == OPT_AUTOBOOL_TRUE) - status_debug("Update our node_announcement for discovered address: %s", - fmt_wireaddr(tmpctx, &discovered_ip)); - maybe_send_own_node_announce(daemon, false); -} - /* Statistically, how many peers to we tell about each channel? */ #define GOSSIP_SPAM_REDUNDANCY 5 @@ -688,13 +634,9 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) &chainparams, &daemon->our_features, &daemon->id, - daemon->rgb, - daemon->alias, - &daemon->announceable, &dev_gossip_time, &dev_fast_gossip, - &dev_fast_gossip_prune, - &daemon->ip_discovery)) { + &dev_fast_gossip_prune)) { master_badmsg(WIRE_GOSSIPD_INIT, msg); } @@ -713,10 +655,6 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) if (daemon->rstate->last_timestamp > timestamp) daemon->rstate->last_timestamp = timestamp; - /* If that announced channels, we can announce ourselves (options - * or addresses might have changed!) */ - maybe_send_own_node_announce(daemon, true); - /* Start the twice- weekly refresh timer. */ notleak(new_reltimer(&daemon->timers, daemon, time_from_sec(GOSSIP_PRUNE_INTERVAL(daemon->rstate->dev_fast_gossip_prune) / 4), @@ -824,10 +762,6 @@ static void handle_txout_reply(struct daemon *daemon, const u8 *msg) /* If we looking specifically for this, we no longer are. */ remove_unknown_scid(daemon->seeker, &scid, good); - - /* 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); } /*~ lightningd tells us when about a gossip message directly, when told to by @@ -874,23 +808,6 @@ static void inject_gossip(struct daemon *daemon, const u8 *msg) take(towire_gossipd_addgossip_reply(NULL, err))); } -static void handle_new_lease_rates(struct daemon *daemon, const u8 *msg) -{ - struct lease_rates *rates = tal(daemon, struct lease_rates); - - if (!fromwire_gossipd_new_lease_rates(msg, rates)) - master_badmsg(WIRE_GOSSIPD_NEW_LEASE_RATES, msg); - - daemon->rates = tal_free(daemon->rates); - if (!lease_rates_empty(rates)) - daemon->rates = rates; - else - tal_free(rates); - - /* Send the update over to the peer */ - maybe_send_own_node_announce(daemon, false); -} - /*~ This is where lightningd tells us that a channel's funding transaction has * been spent. */ static void handle_outpoints_spent(struct daemon *daemon, const u8 *msg) @@ -941,16 +858,9 @@ static struct io_plan *recv_req(struct io_conn *conn, inject_gossip(daemon, msg); goto done; - case WIRE_GOSSIPD_NEW_LEASE_RATES: - handle_new_lease_rates(daemon, msg); - goto done; - case WIRE_GOSSIPD_GET_ADDRS: return handle_get_address(conn, daemon, msg); - case WIRE_GOSSIPD_DISCOVERED_IP: - handle_discovered_ip(daemon, msg); - goto done; case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: if (daemon->developer) { dev_set_max_scids_encode_size(daemon, msg); @@ -1012,14 +922,7 @@ int main(int argc, char *argv[]) daemon->peers = tal(daemon, struct peer_node_id_map); peer_node_id_map_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; - daemon->discovered_ip_v4 = NULL; - daemon->discovered_ip_v6 = NULL; - daemon->ip_discovery = OPT_AUTOBOOL_AUTO; - 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 a9271f72d81e..6d4b6ddbf902 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -56,24 +56,6 @@ struct daemon { /* Timers: we batch gossip, and also refresh announcements */ struct timers timers; - /* Alias (not NUL terminated) and favorite color for node_announcement */ - u8 alias[32]; - u8 rgb[3]; - - /* What addresses we can actually announce. */ - struct wireaddr *announceable; - - /* verified remote_addr as reported by recent peers */ - struct wireaddr *discovered_ip_v4; - struct wireaddr *discovered_ip_v6; - enum opt_autobool ip_discovery; - - /* 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; @@ -82,12 +64,6 @@ struct daemon { /* Features lightningd told us to set. */ struct feature_set *our_features; - - /* 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/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 7b6b4fc82a75..63dabd05ea5b 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -9,14 +9,9 @@ msgtype,gossipd_init,3000 msgdata,gossipd_init,chainparams,chainparams, 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_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, -msgdata,gossipd_init,ip_discovery,u32, # Gossipd tells us all our public channel_updates before init_reply. msgtype,gossipd_init_cupdate,3101 @@ -85,10 +80,6 @@ msgdata,gossipd_addgossip,msg,u8,len msgtype,gossipd_addgossip_reply,3144 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, @@ -97,10 +88,6 @@ msgtype,gossipd_get_addrs_reply,3150 msgdata,gossipd_get_addrs_reply,num,u16, msgdata,gossipd_get_addrs_reply,addrs,wireaddr,num -# 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, - subtype,peer_update subtypedata,peer_update,scid,short_channel_id, subtypedata,peer_update,fee_base,u32, diff --git a/gossipd/queries.c b/gossipd/queries.c index 5f6f3accd305..61d70293a893 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -324,6 +323,30 @@ static void send_reply_channel_range(struct peer *peer, queue_peer_msg(peer, take(msg)); } +/* Helper to get non-signature, non-timestamp parts of (valid!) channel_update */ +void get_cupdate_parts(const u8 *channel_update, + const u8 *parts[2], + size_t sizes[2]) +{ + /* BOLT #7: + * + * 1. type: 258 (`channel_update`) + * 2. data: + * * [`signature`:`signature`] + * * [`chain_hash`:`chain_hash`] + * * [`short_channel_id`:`short_channel_id`] + * * [`u32`:`timestamp`] + *... + */ + /* Note: 2 bytes for `type` field */ + /* We already checked it's valid before accepting */ + assert(tal_count(channel_update) > 2 + 64 + 32 + 8 + 4); + parts[0] = channel_update + 2 + 64; + sizes[0] = 32 + 8; + parts[1] = channel_update + 2 + 64 + 32 + 8 + 4; + sizes[1] = tal_count(channel_update) - (64 + 2 + 32 + 8 + 4); +} + /* BOLT #7: * * The checksum of a `channel_update` is the CRC32C checksum as specified in diff --git a/gossipd/queries.h b/gossipd/queries.h index f2dd5847d03a..727f6ef1023f 100644 --- a/gossipd/queries.h +++ b/gossipd/queries.h @@ -7,6 +7,7 @@ struct channel_update_timestamps; struct daemon; struct io_conn; struct peer; +struct range_query_reply; struct short_channel_id; /* Various handlers when peer fwds a gossip query msg: return is NULL or @@ -60,4 +61,8 @@ struct io_plan *dev_query_channel_range(struct io_conn *conn, * of short_channel_ids we'll encode, using dev_set_max_scids_encode_size. */ void dev_set_max_scids_encode_size(struct daemon *daemon, const u8 *msg); +void get_cupdate_parts(const u8 *channel_update, + const u8 *parts[2], + size_t sizes[2]); + #endif /* LIGHTNING_GOSSIPD_QUERIES_H */ diff --git a/gossipd/routing.c b/gossipd/routing.c index 1e543a7d7b99..65f620b0fd59 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -11,9 +12,9 @@ #include #include #include -#include #include #include +#include #include #ifndef SUPERVERBOSE @@ -294,7 +295,6 @@ struct routing_state *new_routing_state(const tal_t *ctx, rstate->daemon = daemon; rstate->nodes = new_node_map(rstate); rstate->gs = gossip_store_new(rstate); - rstate->local_channel_announced = false; rstate->last_timestamp = 0; rstate->dying_channels = tal_arr(rstate, struct dying_channel, 0); @@ -826,7 +826,6 @@ 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 = local_direction(rstate, chan, NULL); chan->bcast.timestamp = timestamp; /* 0, unless we're loading from store */ @@ -840,7 +839,6 @@ static void add_channel_announce_to_broadcast(struct routing_state *rstate, false, false, addendum); - rstate->local_channel_announced |= is_local; } static void delete_chan_messages_from_store(struct routing_state *rstate, @@ -1272,6 +1270,24 @@ void tell_lightningd_peer_update(struct routing_state *rstate, daemon_conn_send(rstate->daemon->master, take(msg)); } +/* Is this channel_update different from prev (not sigs and timestamps)? */ +static bool cupdate_different(struct gossip_store *gs, + const struct half_chan *hc, + const u8 *cupdate) +{ + const u8 *oparts[2], *nparts[2]; + size_t osizes[2], nsizes[2]; + const u8 *orig; + + /* Get last one we have. */ + orig = gossip_store_get(tmpctx, gs, hc->bcast.index); + get_cupdate_parts(orig, oparts, osizes); + get_cupdate_parts(cupdate, nparts, nsizes); + + return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) + || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]); +} + bool routing_add_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 index, @@ -1710,6 +1726,84 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, return NULL; } +/* Get non-signature, non-timestamp parts of (valid!) node_announcement, + * with TLV broken out separately */ +static void get_nannounce_parts(const u8 *node_announcement, + const u8 *parts[3], + size_t sizes[3]) +{ + size_t len, ad_len; + const u8 *flen, *ad_start; + + /* BOLT #7: + * + * 1. type: 257 (`node_announcement`) + * 2. data: + * * [`signature`:`signature`] + * * [`u16`:`flen`] + * * [`flen*byte`:`features`] + * * [`u32`:`timestamp`] + *... + */ + /* Note: 2 bytes for `type` field */ + /* We already checked it's valid before accepting */ + assert(tal_count(node_announcement) > 2 + 64); + parts[0] = node_announcement + 2 + 64; + + /* Read flen to get size */ + flen = parts[0]; + len = tal_count(node_announcement) - (2 + 64); + sizes[0] = 2 + fromwire_u16(&flen, &len); + assert(flen != NULL && len >= 4); + + /* BOLT-0fe3485a5320efaa2be8cfa0e570ad4d0259cec3 #7: + * + * * [`u32`:`timestamp`] + * * [`point`:`node_id`] + * * [`3*byte`:`rgb_color`] + * * [`32*byte`:`alias`] + * * [`u16`:`addrlen`] + * * [`addrlen*byte`:`addresses`] + * * [`node_ann_tlvs`:`tlvs`] + */ + parts[1] = node_announcement + 2 + 64 + sizes[0] + 4; + + /* Find the end of the addresses */ + ad_start = parts[1] + 33 + 3 + 32; + len = tal_count(node_announcement) + - (2 + 64 + sizes[0] + 4 + 33 + 3 + 32); + ad_len = fromwire_u16(&ad_start, &len); + assert(ad_start != NULL && len >= ad_len); + + sizes[1] = 33 + 3 + 32 + 2 + ad_len; + + /* Is there a TLV ? */ + sizes[2] = len - ad_len; + if (sizes[2] != 0) + parts[2] = parts[1] + sizes[1]; + else + parts[2] = NULL; +} + +/* Is this node_announcement different from prev (not sigs and timestamps)? */ +static bool nannounce_different(struct gossip_store *gs, + const struct node *node, + const u8 *nannounce) +{ + const u8 *oparts[3], *nparts[3]; + size_t osizes[3], nsizes[3]; + const u8 *orig; + + /* Get last one we have. */ + orig = gossip_store_get(tmpctx, gs, node->bcast.index); + get_nannounce_parts(orig, oparts, osizes); + get_nannounce_parts(nannounce, nparts, nsizes); + + return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) + || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]) + || !memeq(oparts[2], osizes[2], nparts[2], nsizes[2]); +} + bool routing_add_node_announcement(struct routing_state *rstate, const u8 *msg TAKES, u32 index, @@ -1793,7 +1887,6 @@ bool routing_add_node_announcement(struct routing_state *rstate, } if (node->bcast.index) { - bool only_tlv_diff; u32 redundant_time; /* The gossip_store should contain a single broadcastable entry @@ -1821,8 +1914,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, /* 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)) { + && !nannounce_different(rstate->gs, node, msg)) { SUPERVERBOSE( "Ignoring redundant nannounce for %s" " (last %u, now %u)", diff --git a/gossipd/routing.h b/gossipd/routing.h index 87f253743892..71062b65837f 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -52,20 +52,6 @@ struct chan { struct amount_sat sat; }; -/* Shadow structure for local channels: owned by the chan above, but kept - * separately to keep `struct chan` minimal since there may be millions - * of non-local channels. */ -struct local_chan { - struct chan *chan; - int direction; - - /* We soft-disable local channels when a peer disconnects */ - bool local_disabled; - - /* Timer if we're deferring an update. */ - struct oneshot *channel_update_timer; -}; - /* Use this instead of tal_free(chan)! */ void free_chan(struct routing_state *rstate, struct chan *chan); @@ -210,9 +196,6 @@ struct routing_state { * we haven't got a channel_update for these yet. */ UINTMAP(struct unupdated_channel *) unupdated_chanmap; - /* Has one of our own channels been announced? */ - bool local_channel_announced; - /* Cache for txout queries that failed. Allows us to skip failed * checks if we get another announcement for the same scid. */ size_t num_txout_failures; diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 6f7c8595738a..95eb5a2a0ede 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -54,14 +54,14 @@ 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 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 get_cupdate_parts */ +void get_cupdate_parts(const u8 *channel_update UNNEEDED, + const u8 *parts[2] UNNEEDED, + size_t sizes[2]) +{ fprintf(stderr, "get_cupdate_parts 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 zombie UNNEEDED, bool spam UNNEEDED, bool dying UNNEEDED, @@ -98,12 +98,6 @@ void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable * /* 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, - const u8 *nannounce UNNEEDED, - bool *only_missing_tlv UNNEEDED) -{ fprintf(stderr, "nannounce_different 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 1516d5da6438..c14c5f360cf5 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -2,7 +2,6 @@ int unused_main(int argc, char *argv[]); #define main unused_main #include "../queries.c" -#include "../gossip_generation.c" #undef main #include #include @@ -49,9 +48,6 @@ struct peer *first_random_peer(struct daemon *daemon UNNEEDED, /* 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_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 get_node */ struct node *get_node(struct routing_state *rstate UNNEEDED, const struct node_id *id UNNEEDED) @@ -61,31 +57,14 @@ 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_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_node_announcement */ -u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node_ann UNNEEDED, - const struct node_id *source_peer TAKES UNNEEDED, - bool *was_unknown UNNEEDED) -{ fprintf(stderr, "handle_node_announcement 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_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 next_random_peer */ struct peer *next_random_peer(struct daemon *daemon UNNEEDED, const struct peer *first UNNEEDED, struct peer_node_id_map_iter *it UNNEEDED) { fprintf(stderr, "next_random_peer called!\n"); abort(); } -/* Generated stub for node_has_broadcastable_channels */ -bool node_has_broadcastable_channels(const struct node *node UNNEEDED) -{ fprintf(stderr, "node_has_broadcastable_channels called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct daemon *daemon UNNEEDED, const struct node_id *source_peer UNNEEDED, @@ -98,30 +77,17 @@ 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 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 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_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 wire_sync_read */ -u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) -{ fprintf(stderr, "wire_sync_read 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[]) diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index ec7b3187628b..b38d990d753d 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -52,11 +52,6 @@ struct peer *first_random_peer(struct daemon *daemon UNNEEDED, /* 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 get_cupdate_parts */ -void get_cupdate_parts(const u8 *channel_update UNNEEDED, - const u8 *parts[2] UNNEEDED, - size_t sizes[2]) -{ fprintf(stderr, "get_cupdate_parts 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-txout_failure.c b/gossipd/test/run-txout_failure.c index 750dc3ce1a6f..d9047c994fda 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -25,14 +25,14 @@ 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 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 get_cupdate_parts */ +void get_cupdate_parts(const u8 *channel_update UNNEEDED, + const u8 *parts[2] UNNEEDED, + size_t sizes[2]) +{ fprintf(stderr, "get_cupdate_parts 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 zombie UNNEEDED, bool spam UNNEEDED, bool dying UNNEEDED, @@ -66,12 +66,6 @@ void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable * /* 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, - const u8 *nannounce UNNEEDED, - bool *only_missing_tlv UNNEEDED) -{ fprintf(stderr, "nannounce_different 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/lightningd/gossip_control.c b/lightningd/gossip_control.c index 42bd13e46a19..db3d8867e581 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -172,7 +172,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_INIT: case WIRE_GOSSIPD_GET_TXOUT_REPLY: case WIRE_GOSSIPD_OUTPOINTS_SPENT: - case WIRE_GOSSIPD_NEW_LEASE_RATES: case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPD_DEV_MEMLEAK: case WIRE_GOSSIPD_DEV_COMPACT_STORE: @@ -187,7 +186,6 @@ 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_DISCOVERED_IP: break; case WIRE_GOSSIPD_INIT_CUPDATE: @@ -282,13 +280,9 @@ void gossip_init(struct lightningd *ld, int connectd_fd) chainparams, ld->our_features, &ld->id, - ld->rgb, - ld->alias, - ld->announceable, ld->dev_gossip_time ? &ld->dev_gossip_time: NULL, ld->dev_fast_gossip, - ld->dev_fast_gossip_prune, - ld->config.ip_discovery); + ld->dev_fast_gossip_prune); subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, gossipd_init_done, NULL); From e715789291a17cb8c7a3ec3c33cbe3ef636e6438 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 29/38] channeld: notify lightningd once we've exchanged reestablish messages. In particular, we were sending `announcement_signatures` before `channel_reestablish`; we allow this because LND used to do it, but it's not spec compliant. Signed-off-by: Rusty Russell --- channeld/channeld.c | 8 +++ channeld/channeld_wire.csv | 3 + lightningd/channel.c | 2 + lightningd/channel.h | 3 + lightningd/channel_control.c | 7 ++ lightningd/channel_gossip.c | 78 +++++++++++---------- lightningd/channel_gossip.h | 7 +- lightningd/peer_control.c | 5 +- lightningd/test/run-invoice-select-inchan.c | 6 +- 9 files changed, 77 insertions(+), 42 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index ccf82a8ad77e..95c7c14c0c05 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -5113,6 +5113,10 @@ static void peer_reconnect(struct peer *peer, /* Now stop, we've been polite long enough. */ if (reestablish_only) { + /* We've reestablished! */ + wire_sync_write(MASTER_FD, + take(towire_channeld_reestablished(NULL))); + /* If we were successfully closing, we still go to closingd. */ if (shutdown_complete(peer)) { send_shutdown_complete(peer); @@ -5126,6 +5130,9 @@ static void peer_reconnect(struct peer *peer, tal_free(send_tlvs); + /* We've reestablished! */ + wire_sync_write(MASTER_FD, take(towire_channeld_reestablished(NULL))); + /* Corner case: we didn't send shutdown before because update_add_htlc * pending, but now they're cleared by restart, and we're actually * complete. In that case, their `shutdown` will trigger us. */ @@ -5673,6 +5680,7 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_GOT_INFLIGHT: case WIRE_CHANNELD_SPLICE_STATE_ERROR: case WIRE_CHANNELD_LOCAL_ANCHOR_INFO: + case WIRE_CHANNELD_REESTABLISHED: break; } master_badmsg(-1, msg); diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 40ec06215cd7..b62d291b17f0 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -77,6 +77,9 @@ msgdata,channeld_init,num_inflights,u16, msgdata,channeld_init,inflights,inflight,num_inflights msgdata,channeld_init,scid_alias,short_channel_id, +# channeld->lightningd: successfully negotated reestablishment. +msgtype,channeld_reestablished,1101 + # master->channeld funding hit new depth(funding locked if >= lock depth) # short_channel_id != NULL once we have 3+ confirmations msgtype,channeld_funding_depth,1002 diff --git a/lightningd/channel.c b/lightningd/channel.c index 7b8e0bc09fdc..97216a03fe02 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -254,6 +254,7 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->state = DUALOPEND_OPEN_INIT; channel->owner = NULL; channel->scb = NULL; + channel->reestablished = false; memset(&channel->billboard, 0, sizeof(channel->billboard)); channel->billboard.transient = tal_fmt(channel, "%s", "Empty channel init'd"); @@ -461,6 +462,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->peer = peer; channel->dbid = dbid; channel->unsaved_dbid = 0; + channel->reestablished = false; channel->error = NULL; channel->open_attempt = NULL; channel->openchannel_signed_cmd = NULL; diff --git a/lightningd/channel.h b/lightningd/channel.h index 058bd4e4fde0..fe18bde77cb8 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -283,6 +283,9 @@ struct channel { /* Block height we saw closing tx at */ u32 *close_blockheight; + /* Channeld said we've successfully exchanged reestablish. */ + bool reestablished; + /* Last known state_change cause */ enum state_change state_change_cause; diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index bd2c84d0cb09..e1e142fad0b9 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1342,6 +1342,9 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_GOT_SHUTDOWN: peer_got_shutdown(sd->channel, msg); break; + case WIRE_CHANNELD_REESTABLISHED: + channel_gossip_channel_reestablished(sd->channel); + break; case WIRE_CHANNELD_SHUTDOWN_COMPLETE: /* We expect 1 fd. */ if (!fds) @@ -1655,6 +1658,10 @@ bool peer_start_channeld(struct channel *channel, get_block_height(ld->topology)); } + /* "Reestablished" if we've just opened. */ + if (!reconnected) + channel_gossip_channel_reestablished(channel); + /* FIXME: DTODO: Use a pointer to a txid instead of zero'ing one out. */ memset(&txid, 0, sizeof(txid)); diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 98f8d2cd42e9..fea95deea31f 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -409,6 +409,10 @@ static void send_channel_announce_sigs(struct channel *channel) if (!channel_state_can_add_htlc(channel->state)) return; + /* Wait until we've exchanged reestablish messages */ + if (!channel->reestablished) + return; + ca = create_channel_announcement(tmpctx, channel, *channel->scid, NULL, NULL, NULL, NULL); @@ -835,45 +839,47 @@ void channel_gossip_init_done(struct lightningd *ld) } } -/* Peer has connected. */ -/* FIXME: We really want "peer has reestablished */ -void channel_gossip_peer_connected(struct peer *peer) +/* Peer has connected and successfully reestablished channel. */ +void channel_gossip_channel_reestablished(struct channel *channel) { - struct channel *channel; + channel->reestablished = true; - list_for_each(&peer->channels, channel, list) { - /* Ignore unsaved channels */ - if (!channel->channel_gossip) - continue; - - switch (channel->channel_gossip->state) { - case CGOSSIP_NOT_USABLE: - continue; - case CGOSSIP_PRIVATE: - case CGOSSIP_NOT_DEEP_ENOUGH: - send_private_cupdate(channel, true); - check_channel_gossip(channel); - continue; - case CGOSSIP_NEED_PEER_SIGS: - /* BOLT #7: - * - upon reconnection (once the above timing - * requirements have been met): - * ... - * - if it has NOT received an - * `announcement_signatures` message: - * - SHOULD retransmit the - * `announcement_signatures` message. - */ - send_private_cupdate(channel, true); - send_channel_announce_sigs(channel); - check_channel_gossip(channel); - continue; - case CGOSSIP_ANNOUNCED: - check_channel_gossip(channel); - continue; - } - fatal("Bad channel_gossip_state %u", channel->channel_gossip->state); + /* Ignore unsaved channels */ + if (!channel->channel_gossip) + return; + + switch (channel->channel_gossip->state) { + case CGOSSIP_NOT_USABLE: + return; + case CGOSSIP_PRIVATE: + case CGOSSIP_NOT_DEEP_ENOUGH: + send_private_cupdate(channel, true); + check_channel_gossip(channel); + return; + case CGOSSIP_NEED_PEER_SIGS: + /* BOLT #7: + * - upon reconnection (once the above timing + * requirements have been met): + * ... + * - if it has NOT received an + * `announcement_signatures` message: + * - SHOULD retransmit the + * `announcement_signatures` message. + */ + send_private_cupdate(channel, true); + send_channel_announce_sigs(channel); + check_channel_gossip(channel); + return; + case CGOSSIP_ANNOUNCED: + check_channel_gossip(channel); + return; } + fatal("Bad channel_gossip_state %u", channel->channel_gossip->state); +} + +void channel_gossip_channel_disconnect(struct channel *channel) +{ + channel->reestablished = false; } /* We *could* send channel_updates for private channels, or diff --git a/lightningd/channel_gossip.h b/lightningd/channel_gossip.h index 0c2c79b40192..9170a98ade55 100644 --- a/lightningd/channel_gossip.h +++ b/lightningd/channel_gossip.h @@ -43,8 +43,11 @@ void channel_gossip_update_from_gossipd(struct channel *channel, * disappointed. */ void channel_gossip_init_done(struct lightningd *ld); -/* Peer has connected. */ -void channel_gossip_peer_connected(struct peer *peer); +/* Peer has connected and successfully reestablished channel. */ +void channel_gossip_channel_reestablished(struct channel *channel); + +/* Peer has disconnected */ +void channel_gossip_channel_disconnect(struct channel *channel); /* Gossipd sent us this channel_update about the peer's side of the channle */ void channel_gossip_set_remote_update(struct lightningd *ld, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 82bf0333c4c1..e4e57a2fe93b 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1401,7 +1401,6 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa } } - channel_gossip_peer_connected(peer); return; send_error: @@ -1933,9 +1932,13 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) * and not free when no more channels. */ p = peer_by_id(ld, &id); if (p) { + struct channel *channel; assert(p->connectd_counter == connectd_counter); log_peer_debug(ld->log, &id, "peer_disconnect_done"); p->connected = PEER_DISCONNECTED; + + list_for_each(&p->channels, channel, list) + channel_gossip_channel_disconnect(channel); } /* If you were trying to connect, it failed. */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 39ff65bbdb54..a19c13e25462 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -84,15 +84,15 @@ void channel_fail_transient(struct channel *channel UNNEEDED, bool disconnect UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "channel_fail_transient called!\n"); abort(); } +/* Generated stub for channel_gossip_channel_disconnect */ +void channel_gossip_channel_disconnect(struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_gossip_channel_disconnect called!\n"); abort(); } /* Generated stub for channel_gossip_get_remote_update */ const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel UNNEEDED) { fprintf(stderr, "channel_gossip_get_remote_update called!\n"); abort(); } /* Generated stub for channel_gossip_node_announce */ void channel_gossip_node_announce(struct lightningd *ld UNNEEDED) { fprintf(stderr, "channel_gossip_node_announce called!\n"); abort(); } -/* Generated stub for channel_gossip_peer_connected */ -void channel_gossip_peer_connected(struct peer *peer UNNEEDED) -{ fprintf(stderr, "channel_gossip_peer_connected called!\n"); abort(); } /* Generated stub for channel_gossip_startup_done */ void channel_gossip_startup_done(struct lightningd *ld UNNEEDED) { fprintf(stderr, "channel_gossip_startup_done called!\n"); abort(); } From 53ec20f04da3543d3fefe2f167c366edf73d7386 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 30/38] lightningd: expose `reestablished` field. This is useful to see if we've really made progress, or just bounced off the peer (e.g. it doesn't know the channel, or we have fallen behind somehow). Changelog-Added: JSON-RPC: `listpeerchannels` new field `reestablished` set once we've exchanged `channel_reestablish` messages. Signed-off-by: Rusty Russell --- doc/lightning-listpeerchannels.7.md | 6 +++++- doc/lightning-sql.7.md | 3 ++- doc/schemas/listpeerchannels.schema.json | 27 ++++++++++++++++++++++++ lightningd/channel_gossip.c | 6 +++++- lightningd/peer_control.c | 3 +++ tests/test_plugin.py | 2 ++ 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/doc/lightning-listpeerchannels.7.md b/doc/lightning-listpeerchannels.7.md index a3c3bc0b6174..c7a2094336ea 100644 --- a/doc/lightning-listpeerchannels.7.md +++ b/doc/lightning-listpeerchannels.7.md @@ -133,6 +133,10 @@ On success, an object containing **channels** is returned. It is an array of ob - **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 **peer\_connected** is *true*: + + - **reestablished** (boolean, optional): True if we have successfully exchanged reestablish messages this connection + If **close\_to** is present: - **close\_to\_addr** (string, optional): The bitcoin address we will close to (present if close\_to\_addr is a standardized address) @@ -215,4 +219,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:a7fc3b30593a04b6b04c1516aeb2039f6ab4e29cf12f81b306a8a068273c3604) +[comment]: # ( SHA256STAMP:8cad14f36d38722182a8fdccc393e93d121d97999d2487decfa09d53c991a8ae) diff --git a/doc/lightning-sql.7.md b/doc/lightning-sql.7.md index dd2aef8d2d98..478635366f1d 100644 --- a/doc/lightning-sql.7.md +++ b/doc/lightning-sql.7.md @@ -352,6 +352,7 @@ The following tables are currently supported: - `local_trimmed` (type `boolean`, sqltype `INTEGER`) - `status` (type `string`, sqltype `TEXT`) - `state` (type `string`, sqltype `TEXT`) + - `reestablished` (type `boolean`, sqltype `INTEGER`) - `close_to_addr` (type `string`, sqltype `TEXT`) - `last_tx_fee_msat` (type `msat`, sqltype `INTEGER`) - `direction` (type `u32`, sqltype `INTEGER`) @@ -530,4 +531,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:5c5e14f8fde75b877080c0c10feb786f1d2fefbb61a62aecb8d003d1d54627e9) +[comment]: # ( SHA256STAMP:f17b33523de930b61d6397a730c40b2254929cf38930629b9be66f3d52947d0e) diff --git a/doc/schemas/listpeerchannels.schema.json b/doc/schemas/listpeerchannels.schema.json index e731f6c2cc21..5588b687ce9c 100644 --- a/doc/schemas/listpeerchannels.schema.json +++ b/doc/schemas/listpeerchannels.schema.json @@ -28,6 +28,7 @@ "type": "boolean", "description": "A boolean flag that is set to true if the peer is online" }, + "reestablished": {}, "state": { "type": "string", "enum": [ @@ -634,6 +635,7 @@ "alias": {}, "peer_id": {}, "peer_connected": {}, + "reestablished": {}, "state": { "type": "string", "enum": [ @@ -679,6 +681,7 @@ "status": {}, "peer_id": {}, "peer_connected": {}, + "reestablished": {}, "state": { "type": "string", "enum": [ @@ -703,6 +706,26 @@ } }, "allOf": [ + { + "if": { + "properties": { + "peer_connected": { + "type": "boolean", + "enum": [ + true + ] + } + } + }, + "then": { + "properties": { + "reestablished": { + "type": "boolean", + "description": "True if we have successfully exchanged reestablish messages this connection" + } + } + } + }, { "if": { "required": [ @@ -716,6 +739,7 @@ "state": {}, "peer_id": {}, "peer_connected": {}, + "reestablished": {}, "scratch_txid": {}, "channel_type": {}, "feerate": {}, @@ -807,6 +831,7 @@ "state": {}, "peer_id": {}, "peer_connected": {}, + "reestablished": {}, "alias": {}, "scratch_txid": {}, "channel_type": {}, @@ -898,6 +923,7 @@ "alias": {}, "peer_id": {}, "peer_connected": {}, + "reestablished": {}, "state": {}, "scratch_txid": {}, "channel_type": {}, @@ -991,6 +1017,7 @@ "state": {}, "peer_id": {}, "peer_connected": {}, + "reestablished": {}, "scratch_txid": {}, "channel_type": {}, "feerate": {}, diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index fea95deea31f..385b6c8f74b5 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -410,8 +410,10 @@ static void send_channel_announce_sigs(struct channel *channel) return; /* Wait until we've exchanged reestablish messages */ - if (!channel->reestablished) + if (!channel->reestablished) { + log_debug(channel->log, "channel_gossip: not sending channel_announcement_sigs until reestablished"); return; + } ca = create_channel_announcement(tmpctx, channel, *channel->scid, NULL, NULL, NULL, NULL); @@ -843,6 +845,7 @@ void channel_gossip_init_done(struct lightningd *ld) void channel_gossip_channel_reestablished(struct channel *channel) { channel->reestablished = true; + log_debug(channel->log, "channel_gossip: reestablished"); /* Ignore unsaved channels */ if (!channel->channel_gossip) @@ -879,6 +882,7 @@ void channel_gossip_channel_reestablished(struct channel *channel) void channel_gossip_channel_disconnect(struct channel *channel) { + log_debug(channel->log, "channel_gossip: NO LONGER reestablished"); channel->reestablished = false; } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e4e57a2fe93b..9d6c1605b795 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -825,6 +825,9 @@ static void json_add_channel(struct lightningd *ld, if (peer) { json_add_node_id(response, "peer_id", &peer->id); json_add_bool(response, "peer_connected", peer->connected == PEER_CONNECTED); + if (peer->connected == PEER_CONNECTED) { + json_add_bool(response, "reestablished", channel->reestablished); + } json_add_channel_type(response, "channel_type", channel->type); if (channel->ignore_fee_limits) { json_add_bool(response, "ignore_fee_limits", channel->ignore_fee_limits); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 0d341e64ee2b..ccc8667b05e3 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -3840,6 +3840,8 @@ def test_sql(node_factory, bitcoind): 'type': 'u64'}, {'name': 'out_fulfilled_msat', 'type': 'msat'}, + {'name': 'reestablished', + 'type': 'boolean'}, {'name': 'close_to_addr', 'type': 'string'}, {'name': 'last_tx_fee_msat', From 42048efc034dc125ea314dff11118ca4e1d231a6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 31/38] lightningd: add last_stable_connection field to db, channel. Signed-off-by: Rusty Russell --- lightningd/channel.c | 5 ++++- lightningd/channel.h | 6 +++++- lightningd/closed_channel.h | 1 + lightningd/opening_control.c | 6 ++++-- wallet/db.c | 1 + wallet/test/run-db.c | 3 ++- wallet/test/run-wallet.c | 3 ++- wallet/wallet.c | 12 +++++++++--- 8 files changed, 28 insertions(+), 9 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 97216a03fe02..c8ba9adfcab5 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -313,6 +313,7 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->lease_commit_sig = NULL; channel->ignore_fee_limits = ld->config.ignore_fee_limits; + channel->last_stable_connection = 0; /* No shachain yet */ channel->their_shachain.id = 0; @@ -449,7 +450,8 @@ struct channel *new_channel(struct peer *peer, u64 dbid, struct amount_msat htlc_maximum_msat, bool ignore_fee_limits, /* NULL or stolen */ - struct peer_update *peer_update STEALS) + struct peer_update *peer_update STEALS, + u64 last_stable_connection) { struct channel *channel = tal(peer->ld, struct channel); struct amount_msat htlc_min, htlc_max; @@ -600,6 +602,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->close_blockheight = NULL; channel->state_change_cause = reason; channel->ignore_fee_limits = ignore_fee_limits; + channel->last_stable_connection = last_stable_connection; /* Populate channel->channel_gossip */ channel_gossip_init(channel, take(peer_update)); diff --git a/lightningd/channel.h b/lightningd/channel.h index fe18bde77cb8..24c3113c5791 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -310,6 +310,9 @@ struct channel { /* Do we allow the peer to set any fee it wants? */ bool ignore_fee_limits; + + /* Last time we had a stable connection, if any (0 = none) */ + u64 last_stable_connection; }; /* Is channel owned (and should be talking to peer) */ @@ -390,7 +393,8 @@ struct channel *new_channel(struct peer *peer, u64 dbid, struct amount_msat htlc_maximum_msat, bool ignore_fee_limits, /* NULL or stolen */ - struct peer_update *peer_update STEALS); + struct peer_update *peer_update STEALS, + u64 last_stable_connection); /* new_inflight - Create a new channel_inflight for a channel */ struct channel_inflight *new_inflight(struct channel *channel, diff --git a/lightningd/closed_channel.h b/lightningd/closed_channel.h index 44a2269fe184..13f58d62b4fe 100644 --- a/lightningd/closed_channel.h +++ b/lightningd/closed_channel.h @@ -27,6 +27,7 @@ struct closed_channel { const struct channel_type *type; enum state_change state_change_cause; bool leased; + u64 last_stable_connection; }; #endif /* LIGHTNING_LIGHTNINGD_CLOSED_CHANNEL_H */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 5410f6468ce5..22a92107d356 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -235,7 +235,8 @@ wallet_commit_channel(struct lightningd *ld, ld->config.htlc_minimum_msat, ld->config.htlc_maximum_msat, ld->config.ignore_fee_limits, - NULL); + NULL, + 0); /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); @@ -1504,7 +1505,8 @@ static struct channel *stub_chan(struct command *cmd, ld->config.htlc_minimum_msat, ld->config.htlc_maximum_msat, false, - NULL); + NULL, + 0); /* We don't want to gossip about this, ever. */ channel->channel_gossip = tal_free(channel->channel_gossip); diff --git a/wallet/db.c b/wallet/db.c index 3215f86cdea4..53b83481b67e 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1016,6 +1016,7 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE channels ADD remote_cltv_expiry_delta INTEGER DEFAULT NULL;"), NULL}, {SQL("ALTER TABLE channels ADD remote_htlc_maximum_msat BIGINT DEFAULT NULL;"), NULL}, {SQL("ALTER TABLE channels ADD remote_htlc_minimum_msat BIGINT DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE channels ADD last_stable_connection BIGINT DEFAULT 0;"), NULL}, }; /** diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 6d23b9000c33..50fa0e6917a8 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -184,7 +184,8 @@ struct channel *new_channel(struct peer *peer UNNEEDED, u64 dbid UNNEEDED, struct amount_msat htlc_maximum_msat UNNEEDED, bool ignore_fee_limits UNNEEDED, /* NULL or stolen */ - struct peer_update *peer_update STEALS UNNEEDED) + struct peer_update *peer_update STEALS UNNEEDED, + u64 last_stable_connection UNNEEDED) { fprintf(stderr, "new_channel called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit */ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 84649549a8cd..3c3320acf7e0 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -2007,7 +2007,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) AMOUNT_MSAT(0), AMOUNT_MSAT(-1ULL), false, - NULL); + NULL, + 0); db_begin_transaction(w->db); CHECK(!wallet_err); wallet_channel_insert(w, chan); diff --git a/wallet/wallet.c b/wallet/wallet.c index 0279e8180f6c..da9d789c86dc 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1763,7 +1763,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm htlc_minimum_msat, htlc_maximum_msat, ignore_fee_limits, - remote_update); + remote_update, + db_col_u64(stmt, "last_stable_connection")); if (!wallet_channel_load_inflights(w, chan)) { tal_free(chan); @@ -1800,6 +1801,7 @@ static struct closed_channel *wallet_stmt2closed_channel(const tal_t *ctx, cc->our_msat = db_col_amount_msat(stmt, "msatoshi_local"); cc->msat_to_us_min = db_col_amount_msat(stmt, "msatoshi_to_us_min"); cc->msat_to_us_max = db_col_amount_msat(stmt, "msatoshi_to_us_max"); + cc->last_stable_connection = db_col_u64(stmt, "last_stable_connection"); /* last_tx is null for stub channels used for recovering funds through * Static channel backups. */ if (!db_col_is_null(stmt, "last_tx")) @@ -1845,6 +1847,7 @@ struct closed_channel **wallet_load_closed_channels(const tal_t *ctx, ", channel_type" ", state_change_reason" ", lease_commit_sig" + ", last_stable_connection" " FROM channels" " LEFT JOIN peers p ON p.id = peer_id" " WHERE state = ?;")); @@ -1956,6 +1959,7 @@ static bool wallet_channels_load_active(struct wallet *w) ", remote_cltv_expiry_delta" ", remote_htlc_minimum_msat" ", remote_htlc_maximum_msat" + ", last_stable_connection" " FROM channels" " WHERE state != ?;")); //? 0 db_bind_int(stmt, CLOSED); @@ -2275,8 +2279,9 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " remote_feerate_ppm=?," // 48 " remote_cltv_expiry_delta=?," // 49 " remote_htlc_minimum_msat=?," // 50 - " remote_htlc_maximum_msat=?" // 51 - " WHERE id=?")); // 52 + " remote_htlc_maximum_msat=?," + " last_stable_connection=?" + " WHERE id=?")); db_bind_u64(stmt, chan->their_shachain.id); if (chan->scid) db_bind_short_channel_id(stmt, chan->scid); @@ -2372,6 +2377,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt); db_bind_null(stmt); } + db_bind_u64(stmt, chan->last_stable_connection); db_bind_u64(stmt, chan->dbid); db_exec_prepared_v2(take(stmt)); From a08c16dd676fcbe015ff76e45b81871f11520d07 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:19 +1030 Subject: [PATCH 32/38] lightningd: set last_stable_connection 1 minute after channel reestablished. This is a nice reflection of channel stability: in particular, worse case ping time is 45 seconds, so we should have had some traffic. Signed-off-by: Rusty Russell --- lightningd/channel.c | 2 ++ lightningd/channel.h | 1 + lightningd/channel_gossip.c | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index c8ba9adfcab5..6b025a4e052a 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -314,6 +314,7 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->lease_commit_sig = NULL; channel->ignore_fee_limits = ld->config.ignore_fee_limits; channel->last_stable_connection = 0; + channel->stable_conn_timer = NULL; /* No shachain yet */ channel->their_shachain.id = 0; @@ -603,6 +604,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->state_change_cause = reason; channel->ignore_fee_limits = ignore_fee_limits; channel->last_stable_connection = last_stable_connection; + channel->stable_conn_timer = NULL; /* Populate channel->channel_gossip */ channel_gossip_init(channel, take(peer_update)); diff --git a/lightningd/channel.h b/lightningd/channel.h index 24c3113c5791..9798532936f8 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -313,6 +313,7 @@ struct channel { /* Last time we had a stable connection, if any (0 = none) */ u64 last_stable_connection; + struct oneshot *stable_conn_timer; }; /* Is channel owned (and should be talking to peer) */ diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 385b6c8f74b5..5905d49b7593 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -841,10 +841,23 @@ void channel_gossip_init_done(struct lightningd *ld) } } +static void channel_reestablished_stable(struct channel *channel) +{ + channel->stable_conn_timer = NULL; + channel->last_stable_connection = time_now().ts.tv_sec; + wallet_channel_save(channel->peer->ld->wallet, channel); +} + /* Peer has connected and successfully reestablished channel. */ void channel_gossip_channel_reestablished(struct channel *channel) { channel->reestablished = true; + tal_free(channel->stable_conn_timer); + channel->stable_conn_timer = new_reltimer(channel->peer->ld->timers, + channel, time_from_sec(60), + channel_reestablished_stable, + channel); + log_debug(channel->log, "channel_gossip: reestablished"); /* Ignore unsaved channels */ @@ -882,7 +895,7 @@ void channel_gossip_channel_reestablished(struct channel *channel) void channel_gossip_channel_disconnect(struct channel *channel) { - log_debug(channel->log, "channel_gossip: NO LONGER reestablished"); + channel->stable_conn_timer = tal_free(channel->stable_conn_timer); channel->reestablished = false; } From 9b31f9642f0ce94530049efa8168f749461e8fb8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:20 +1030 Subject: [PATCH 33/38] lightningd: expose last_stable_connection in RPC. This isuseful to find completely dead channels which are worthy of closing. Changelog-Added: JSON-RPC: `listpeerchannels` field `last_stable_connection` showing when we last held an established channel for a minute or more. Changelog-Added: JSON-RPC: `listclosedchannels` field `last_stable_connection` showing when we last held an established channel for a minute or more. Signed-off-by: Rusty Russell --- .msggen.json | 10 + cln-grpc/proto/node.proto | 2 + cln-grpc/src/convert.rs | 2 + cln-rpc/src/model.rs | 4 + contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py | 380 +++++++++--------- contrib/pyln-testing/pyln/testing/grpc2py.py | 2 + doc/lightning-listclosedchannels.7.md | 3 +- doc/lightning-listpeerchannels.7.md | 3 +- doc/lightning-sql.7.md | 4 +- doc/schemas/listclosedchannels.schema.json | 5 + doc/schemas/listpeerchannels.schema.json | 9 + lightningd/closed_channel.c | 4 + lightningd/peer_control.c | 5 + tests/test_connection.py | 31 ++ tests/test_plugin.py | 6 +- 15 files changed, 276 insertions(+), 194 deletions(-) diff --git a/.msggen.json b/.msggen.json index e91e8fbc1828..6afde91ade84 100644 --- a/.msggen.json +++ b/.msggen.json @@ -939,6 +939,7 @@ "ListClosedChannels.closedchannels[].funding_txid": 12, "ListClosedChannels.closedchannels[].last_commitment_fee_msat": 23, "ListClosedChannels.closedchannels[].last_commitment_txid": 22, + "ListClosedChannels.closedchannels[].last_stable_connection": 25, "ListClosedChannels.closedchannels[].leased": 14, "ListClosedChannels.closedchannels[].max_to_us_msat": 21, "ListClosedChannels.closedchannels[].min_to_us_msat": 20, @@ -1147,6 +1148,7 @@ "ListPeerChannels.channels[].inflight[]": 16, "ListPeerChannels.channels[].initial_feerate": 12, "ListPeerChannels.channels[].last_feerate": 13, + "ListPeerChannels.channels[].last_stable_connection": 56, "ListPeerChannels.channels[].max_accepted_htlcs": 40, "ListPeerChannels.channels[].max_to_us_msat": 25, "ListPeerChannels.channels[].max_total_htlc_in_msat": 30, @@ -3676,6 +3678,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListClosedChannels.closedchannels[].last_stable_connection": { + "added": "v24.02", + "deprecated": false + }, "ListClosedChannels.closedchannels[].leased": { "added": "pre-v0.10.1", "deprecated": false @@ -4408,6 +4414,10 @@ "added": "v23.02", "deprecated": false }, + "ListPeerChannels.channels[].last_stable_connection": { + "added": "v24.02", + "deprecated": false + }, "ListPeerChannels.channels[].max_accepted_htlcs": { "added": "v23.02", "deprecated": false diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index e8228ebff95d..ff2fc7dce11c 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1235,6 +1235,7 @@ message ListpeerchannelsChannels { optional Amount out_offered_msat = 49; optional uint64 out_payments_fulfilled = 50; optional Amount out_fulfilled_msat = 51; + optional uint64 last_stable_connection = 56; repeated ListpeerchannelsChannelsHtlcs htlcs = 52; optional string close_to_addr = 53; } @@ -1345,6 +1346,7 @@ message ListclosedchannelsClosedchannels { optional bytes last_commitment_txid = 22; optional Amount last_commitment_fee_msat = 23; ListclosedchannelsClosedchannelsClose_cause close_cause = 24; + optional uint64 last_stable_connection = 25; } message ListclosedchannelsClosedchannelsAlias { diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index e7c207290f5e..bb6678c25c0e 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1168,6 +1168,7 @@ impl From for pb::ListpeerchannelsChannels out_offered_msat: c.out_offered_msat.map(|f| f.into()), // Rule #2 for type msat? 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? + last_stable_connection: c.last_stable_connection, // Rule #2 for type u64? // Field: ListPeerChannels.channels[].htlcs[] 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? @@ -1222,6 +1223,7 @@ impl From for pb::Listclosedchannel last_commitment_txid: c.last_commitment_txid.map(|v| >::as_ref(&v).to_vec()), // Rule #2 for type hash? last_commitment_fee_msat: c.last_commitment_fee_msat.map(|f| f.into()), // Rule #2 for type msat? close_cause: c.close_cause as i32, + last_stable_connection: c.last_stable_connection, // Rule #2 for type u64? } } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 23aea73eabdb..287c0b94429b 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -4676,6 +4676,8 @@ pub mod responses { pub out_payments_fulfilled: Option, #[serde(skip_serializing_if = "Option::is_none")] pub out_fulfilled_msat: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub last_stable_connection: Option, #[serde(skip_serializing_if = "crate::is_none_or_empty")] pub htlcs: Option>, #[serde(skip_serializing_if = "Option::is_none")] @@ -4788,6 +4790,8 @@ pub mod responses { pub last_commitment_fee_msat: Option, // Path `ListClosedChannels.closedchannels[].close_cause` pub close_cause: ListclosedchannelsClosedchannelsClose_cause, + #[serde(skip_serializing_if = "Option::is_none")] + pub last_stable_connection: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py index 9e1c6aad2a5a..bf554a214a46 100644 --- a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py +++ b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py @@ -14,7 +14,7 @@ from pyln.grpc import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xc1\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x61lias\x18\x02 \x01(\tH\x00\x88\x01\x01\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\x33\n\x0cour_features\x18\n \x01(\x0b\x32\x18.cln.GetinfoOur_featuresH\x01\x88\x01\x01\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\x02\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_aliasB\x0f\n\r_our_featuresB\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\"\xc4\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\"G\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\x42\n\n\x08_address\"\x8a\x02\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\"_\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\r\n\tWEBSOCKET\x10\x05\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\"\x8e\x02\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x00\x88\x01\x01\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\x01\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x42\x0f\n\r_num_channelsB\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\"\x95\x18\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\x38\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\n\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0c\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\r\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12\x38\n\x07\x66unding\x18\x13 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH \x88\x01\x01\x12\x34\n\x05\x61lias\x18\x32 \x01(\x0b\x32 .cln.ListpeersPeersChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\"\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\"\xe0\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\n\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0b\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\x0c\x42\x0f\n\r_scratch_txidB\n\n\x08_feerateB\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\n\n\x08_fundingB\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\x08\n\x06_aliasB\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\"\xf3\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\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x00\x88\x01\x01\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\x42\x10\n\x0e_splice_amount\"\x9b\x02\n\x1dListpeersPeersChannelsFunding\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\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\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\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\"\xf1\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\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"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\"\x83\x03\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\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\xab\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\x17\n\nchannel_id\x18\t \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x01\x88\x01\x01\x42\r\n\x0b_channel_idB\x13\n\x11_short_channel_id\"\xdd\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\x1a\n\rlocalinvreqid\x18\x0b \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\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xad\x05\n\x0fSendpayResponse\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x01\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x02\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\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x05\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\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x0b\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\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\"\xb3\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\x11\n\tdirection\x18\x10 \x01(\r\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\"\xb4\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\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\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\"\xfe\x05\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\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12;\n\rpaid_outpoint\x18\x11 \x01(\x0b\x32\x1f.cln.CreateinvoicePaid_outpointH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\t\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\n\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\x10\n\x0e_created_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"X\n\x1a\x43reateinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\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\"\x17\n\x15\x44\x61tastoreusageRequest\"k\n\x16\x44\x61tastoreusageResponse\x12>\n\x0e\x64\x61tastoreusage\x18\x01 \x01(\x0b\x32!.cln.DatastoreusageDatastoreusageH\x00\x88\x01\x01\x42\x11\n\x0f_datastoreusage\"b\n\x1c\x44\x61tastoreusageDatastoreusage\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0btotal_bytes\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x06\n\x04_keyB\x0e\n\x0c_total_bytes\"\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\"\xa1\x04\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\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x04\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x05\x88\x01\x01\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\x06\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x07\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\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xfa\x01\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\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\x95\x03\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\x1a\n\rcreated_index\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x03\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x05\x88\x01\x01\x42\x10\n\x0e_created_indexB\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\"\xde\x02\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\x12>\n\x05index\x18\x05 \x01(\x0e\x32*.cln.ListinvoicesRequest.ListinvoicesIndexH\x04\x88\x01\x01\x12\x12\n\x05start\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x12\n\x05limit\x18\x07 \x01(\rH\x06\x88\x01\x01\"-\n\x11ListinvoicesIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xd4\x06\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\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x07\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x08\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\t\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\n\x88\x01\x01\x12\x42\n\rpaid_outpoint\x18\x12 \x01(\x0b\x32&.cln.ListinvoicesInvoicesPaid_outpointH\x0b\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x0c\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\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"_\n!ListinvoicesInvoicesPaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\x8a\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12*\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x17.cln.SendonionFirst_hop\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\x1a\n\rlocalinvreqid\x18\r \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\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xe7\x04\n\x11SendonionResponse\x12\x1a\n\rcreated_index\x18\x0e \x01(\x04H\x00\x88\x01\x01\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\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x02\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\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0f \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\t\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\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\x10\n\x0e_updated_indexB\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\"\xa0\x03\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\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListsendpaysRequest.ListsendpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\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\"-\n\x11ListsendpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd0\x05\n\x14ListsendpaysPayments\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x02\x88\x01\x01\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\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\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\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\n\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\x10\n\x0e_created_indexB\t\n\x07_partidB\x10\n\x0e_updated_indexB\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\"\xf8\x01\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\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.ListtransactionsTransactionsOutputs\"S\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\"l\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\"\xda\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\x1a\n\rlocalinvreqid\x18\x0e \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\x10\n\x0e_localinvreqidB\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\"\xe8\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\"P\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\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\"\xbf\x05\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\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12<\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32 .cln.WaitanyinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\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\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"Y\n\x1bWaitanyinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xb0\x05\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\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12\x39\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1d.cln.WaitinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\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\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"V\n\x18WaitinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\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\"\x8e\x05\n\x13WaitsendpayResponse\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x01\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\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\x04\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x05\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\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x10\n\x0e_created_indexB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_updated_indexB\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\"\x97\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\"3\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x08\n\x04P2TR\x10\x03\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"M\n\x0fNewaddrResponse\x12\x11\n\x04p2tr\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_p2trB\t\n\x07_bech32\"\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\"\x82\x03\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\x12&\n\textratlvs\x18\t \x01(\x0b\x32\x0e.cln.TlvStreamH\x06\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_routehintsB\x0c\n\n_extratlvs\"\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\"\xa4\x03\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\x12\x17\n\nnonwrapped\x18\t \x01(\x08H\x05\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x06\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_changeB\r\n\x0b_nonwrappedB\x19\n\x17_opening_anchor_channel\"\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\"\x9b\x03\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\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x05\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_changeB\x19\n\x17_opening_anchor_channel\"\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\"1\n\x17ListpeerchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"K\n\x18ListpeerchannelsResponse\x12/\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1d.cln.ListpeerchannelsChannels\"\xa2\x1a\n\x18ListpeerchannelsChannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0epeer_connected\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12O\n\x05state\x18\x03 \x01(\x0e\x32;.cln.ListpeerchannelsChannels.ListpeerchannelsChannelsStateH\x02\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x04 \x01(\x0cH\x03\x88\x01\x01\x12:\n\x07updates\x18\x37 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsUpdatesH\x04\x88\x01\x01\x12\x1e\n\x11ignore_fee_limits\x18\x36 \x01(\x08H\x05\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x06\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x08\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\t\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\n\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\x0b\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\x0c\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\r\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\x0e\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\x0f\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x10\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x11\x88\x01\x01\x12%\n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSideH\x12\x88\x01\x01\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\x13\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x14\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x1a\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1e\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x1f\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18# \x01(\x0b\x32\x0b.cln.AmountH!\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18$ \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18% \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12 \n\x13their_to_self_delay\x18& \x01(\rH$\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\' \x01(\rH%\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18( \x01(\rH&\x88\x01\x01\x12\x36\n\x05\x61lias\x18) \x01(\x0b\x32\".cln.ListpeerchannelsChannelsAliasH\'\x88\x01\x01\x12\x0e\n\x06status\x18+ \x03(\t\x12 \n\x13in_payments_offered\x18, \x01(\x04H(\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\x30 \x01(\x04H,\x88\x01\x01\x12*\n\x10out_offered_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH-\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18\x32 \x01(\x04H.\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18\x33 \x01(\x0b\x32\x0b.cln.AmountH/\x88\x01\x01\x12\x31\n\x05htlcs\x18\x34 \x03(\x0b\x32\".cln.ListpeerchannelsChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18\x35 \x01(\tH0\x88\x01\x01\"\x80\x03\n\x1dListpeerchannelsChannelsState\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\n\x12\x1c\n\x18\x43HANNELD_AWAITING_SPLICE\x10\x0b\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0c\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\rB\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\n\n\x08_updatesB\x14\n\x12_ignore_fee_limitsB\n\n\x08_feerateB\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_openerB\t\n\x07_closerB\n\n\x08_fundingB\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\x08\n\x06_aliasB\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\"\xb6\x01\n\x1fListpeerchannelsChannelsUpdates\x12=\n\x05local\x18\x01 \x01(\x0b\x32).cln.ListpeerchannelsChannelsUpdatesLocalH\x00\x88\x01\x01\x12?\n\x06remote\x18\x02 \x01(\x0b\x32*.cln.ListpeerchannelsChannelsUpdatesRemoteH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xe7\x02\n$ListpeerchannelsChannelsUpdatesLocal\x12+\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x14\n\x12_cltv_expiry_deltaB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionths\"\xe8\x02\n%ListpeerchannelsChannelsUpdatesRemote\x12+\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x14\n\x12_cltv_expiry_deltaB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionths\"]\n\x1fListpeerchannelsChannelsFeerate\x12\x12\n\x05perkw\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05perkb\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x08\n\x06_perkwB\x08\n\x06_perkb\"\x80\x03\n ListpeerchannelsChannelsInflight\x12\x19\n\x0c\x66unding_txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x03 \x01(\tH\x02\x88\x01\x01\x12,\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x04\x88\x01\x01\x12*\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x06 \x01(\x0cH\x06\x88\x01\x01\x42\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\n\n\x08_feerateB\x15\n\x13_total_funding_msatB\x10\n\x0e_splice_amountB\x13\n\x11_our_funding_msatB\x0f\n\r_scratch_txid\"\xd2\x02\n\x1fListpeerchannelsChannelsFunding\x12%\n\x0bpushed_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12*\n\x10local_funds_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11remote_funds_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x13\n\x11_local_funds_msatB\x14\n\x12_remote_funds_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"]\n\x1dListpeerchannelsChannelsAlias\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\"\xe2\x03\n\x1dListpeerchannelsChannelsHtlcs\x12\x61\n\tdirection\x18\x01 \x01(\x0e\x32I.cln.ListpeerchannelsChannelsHtlcs.ListpeerchannelsChannelsHtlcsDirectionH\x00\x88\x01\x01\x12\x0f\n\x02id\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x05 \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\"\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcStateH\x07\x88\x01\x01\"9\n&ListpeerchannelsChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x0c\n\n_directionB\x05\n\x03_idB\x0e\n\x0c_amount_msatB\t\n\x07_expiryB\x0f\n\r_payment_hashB\x10\n\x0e_local_trimmedB\t\n\x07_statusB\x08\n\x06_state\"3\n\x19ListclosedchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"[\n\x1aListclosedchannelsResponse\x12=\n\x0e\x63losedchannels\x18\x01 \x03(\x0b\x32%.cln.ListclosedchannelsClosedchannels\"\xb2\t\n ListclosedchannelsClosedchannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x12>\n\x05\x61lias\x18\x04 \x01(\x0b\x32*.cln.ListclosedchannelsClosedchannelsAliasH\x02\x88\x01\x01\x12 \n\x06opener\x18\x05 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x06 \x01(\x0e\x32\x10.cln.ChannelSideH\x03\x88\x01\x01\x12\x0f\n\x07private\x18\x07 \x01(\x08\x12\x1f\n\x17total_local_commitments\x18\t \x01(\x04\x12 \n\x18total_remote_commitments\x18\n \x01(\x04\x12\x18\n\x10total_htlcs_sent\x18\x0b \x01(\x04\x12\x14\n\x0c\x66unding_txid\x18\x0c \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\r \x01(\r\x12\x0e\n\x06leased\x18\x0e \x01(\x08\x12/\n\x15\x66unding_fee_paid_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12/\n\x15\x66unding_fee_rcvd_msat\x18\x10 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12-\n\x13\x66unding_pushed_msat\x18\x11 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1f\n\ntotal_msat\x18\x12 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x66inal_to_us_msat\x18\x13 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emin_to_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emax_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14last_commitment_txid\x18\x16 \x01(\x0cH\x07\x88\x01\x01\x12\x32\n\x18last_commitment_fee_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x66\n\x0b\x63lose_cause\x18\x18 \x01(\x0e\x32Q.cln.ListclosedchannelsClosedchannels.ListclosedchannelsClosedchannelsClose_cause\"v\n+ListclosedchannelsClosedchannelsClose_cause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\n\n\x08_peer_idB\x13\n\x11_short_channel_idB\x08\n\x06_aliasB\t\n\x07_closerB\x18\n\x16_funding_fee_paid_msatB\x18\n\x16_funding_fee_rcvd_msatB\x16\n\x14_funding_pushed_msatB\x17\n\x15_last_commitment_txidB\x1b\n\x19_last_commitment_fee_msat\"e\n%ListclosedchannelsClosedchannelsAlias\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\"L\n\x10\x44\x65\x63odepayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x04\n\x11\x44\x65\x63odepayResponse\x12\x10\n\x08\x63urrency\x18\x01 \x01(\t\x12\x12\n\ncreated_at\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\x04\x12\r\n\x05payee\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x11\n\tsignature\x18\x07 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x1d\n\x15min_final_cltv_expiry\x18\n \x01(\r\x12\x1b\n\x0epayment_secret\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\r \x01(\x0cH\x05\x88\x01\x01\x12*\n\tfallbacks\x18\x0e \x03(\x0b\x32\x17.cln.DecodepayFallbacks\x12\"\n\x05\x65xtra\x18\x10 \x03(\x0b\x32\x13.cln.DecodepayExtraB\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x13\n\x11_description_hashB\x11\n\x0f_payment_secretB\x0b\n\t_featuresB\x13\n\x11_payment_metadata\"\xd0\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"N\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\xe8!\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12+\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x16.cln.DecodeOffer_paths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x37\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1c.cln.DecodeInvoice_fallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\x12\x16\n\tdecrypted\x18L \x01(\x0cH=\x88\x01\x01\"\x83\x01\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x12\x15\n\x11\x45MERGENCY_RECOVER\x10\x05\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hexB\x0c\n\n_decrypted\"<\n\x11\x44\x65\x63odeOffer_paths\x12\x15\n\rfirst_node_id\x18\x01 \x01(\x0c\x12\x10\n\x08\x62linding\x18\x02 \x01(\x0c\"\x8a\x01\n\x1f\x44\x65\x63odeOffer_recurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"T\n\x17\x44\x65\x63odeInvoice_pathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"Y\n\x17\x44\x65\x63odeInvoice_fallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"w\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x42,\n*_warning_invoice_fallbacks_version_invalid\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"=\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\"\x9c\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x46\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32\".cln.FeeratesOnchain_fee_estimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\xd3\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\xd3\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\x9b\x02\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\x30\n#unilateral_close_nonanchor_satoshis\x18\x06 \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\x42&\n$_unilateral_close_nonanchor_satoshis\"\xe9\x02\n\x13\x46\x65tchinvoiceRequest\x12\r\n\x05offer\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x15\n\x08quantity\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x1f\n\x12recurrence_counter\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x10recurrence_start\x18\x05 \x01(\x01H\x03\x88\x01\x01\x12\x1d\n\x10recurrence_label\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07timeout\x18\x07 \x01(\x01H\x05\x88\x01\x01\x12\x17\n\npayer_note\x18\x08 \x01(\tH\x06\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x0b\n\t_quantityB\x15\n\x13_recurrence_counterB\x13\n\x11_recurrence_startB\x13\n\x11_recurrence_labelB\n\n\x08_timeoutB\r\n\x0b_payer_note\"\x9a\x01\n\x14\x46\x65tchinvoiceResponse\x12\x0f\n\x07invoice\x18\x01 \x01(\t\x12)\n\x07\x63hanges\x18\x02 \x01(\x0b\x32\x18.cln.FetchinvoiceChanges\x12\x36\n\x0bnext_period\x18\x03 \x01(\x0b\x32\x1c.cln.FetchinvoiceNext_periodH\x00\x88\x01\x01\x42\x0e\n\x0c_next_period\"\x82\x02\n\x13\x46\x65tchinvoiceChanges\x12!\n\x14\x64\x65scription_appended\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0evendor_removed\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06vendor\x18\x04 \x01(\tH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x17\n\x15_description_appendedB\x0e\n\x0c_descriptionB\x11\n\x0f_vendor_removedB\t\n\x07_vendorB\x0e\n\x0c_amount_msat\"~\n\x17\x46\x65tchinvoiceNext_period\x12\x0f\n\x07\x63ounter\x18\x01 \x01(\x04\x12\x11\n\tstarttime\x18\x02 \x01(\x04\x12\x0f\n\x07\x65ndtime\x18\x03 \x01(\x04\x12\x17\n\x0fpaywindow_start\x18\x04 \x01(\x04\x12\x15\n\rpaywindow_end\x18\x05 \x01(\x04\"\xfb\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\x12\x14\n\x0c\x63hannel_type\x18\x0e \x03(\rB\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\"\xe5\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\x37\n\x0c\x63hannel_type\x18\x07 \x01(\x0b\x32\x1c.cln.FundchannelChannel_typeH\x00\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x02\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_toB\x0b\n\t_mindepth\"L\n\x17\x46undchannelChannel_type\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\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(\rH\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\"\xb7\x03\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\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListforwardsRequest.ListforwardsIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\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\"-\n\x11ListforwardsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channelB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xba\x05\n\x14ListforwardsForwards\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x00\x88\x01\x01\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x01\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\x02\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x04\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x05\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x07\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\x10\n\x0e_created_indexB\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x10\n\x0e_updated_indexB\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\"\xff\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%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\t\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\n\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\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"*\n\x10ListhtlcsRequest\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListhtlcsResponse\x12\"\n\x05htlcs\x18\x01 \x03(\x0b\x32\x13.cln.ListhtlcsHtlcs\"\x89\x02\n\x0eListhtlcsHtlcs\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12>\n\tdirection\x18\x05 \x01(\x0e\x32+.cln.ListhtlcsHtlcs.ListhtlcsHtlcsDirection\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1d\n\x05state\x18\x07 \x01(\x0e\x32\x0e.cln.HtlcState\"*\n\x17ListhtlcsHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xaa\x02\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\x12\x1c\n\x0fignorefeelimits\x18\x07 \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelayB\x12\n\x10_ignorefeelimits\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\xca\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\x1e\n\x11ignore_fee_limits\x18\n \x01(\x08H\x01\x88\x01\x01\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x02\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\x03\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x14\n\x12_ignore_fee_limitsB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\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\"O\n\x16WaitblockheightRequest\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\x12\x14\n\x07timeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_timeout\".\n\x17WaitblockheightResponse\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\"\xf9\x01\n\x0bWaitRequest\x12\x31\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitSubsystem\x12\x31\n\tindexname\x18\x02 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitIndexname\x12\x11\n\tnextvalue\x18\x03 \x01(\x04\"9\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\"6\n\rWaitIndexname\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x12\x0b\n\x07\x44\x45LETED\x10\x02\"\xe3\x01\n\x0cWaitResponse\x12\x32\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1f.cln.WaitResponse.WaitSubsystem\x12\x14\n\x07\x63reated\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07updated\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07\x64\x65leted\x18\x04 \x01(\x04H\x02\x88\x01\x01\"9\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\x42\n\n\x08_createdB\n\n\x08_updatedB\n\n\x08_deleted\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse\"\xa7\x01\n\x18PreapprovekeysendRequest\x12\x18\n\x0b\x64\x65stination\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_destinationB\x0f\n\r_payment_hashB\x0e\n\x0c_amount_msat\"\x1b\n\x19PreapprovekeysendResponse\":\n\x18PreapproveinvoiceRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_bolt11\"\x1b\n\x19PreapproveinvoiceResponse\"\x15\n\x13StaticbackupRequest\"#\n\x14StaticbackupResponse\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\x32\xa2\x1f\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\x12K\n\x0e\x44\x61tastoreUsage\x12\x1a.cln.DatastoreusageRequest\x1a\x1b.cln.DatastoreusageResponse\"\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\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\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\x45\n\x0c\x46\x65tchInvoice\x12\x18.cln.FetchinvoiceRequest\x1a\x19.cln.FetchinvoiceResponse\"\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\tListHtlcs\x12\x15.cln.ListhtlcsRequest\x1a\x16.cln.ListhtlcsResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12N\n\x0fWaitBlockHeight\x12\x1b.cln.WaitblockheightRequest\x1a\x1c.cln.WaitblockheightResponse\"\x00\x12-\n\x04Wait\x12\x10.cln.WaitRequest\x1a\x11.cln.WaitResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x12T\n\x11PreApproveKeysend\x12\x1d.cln.PreapprovekeysendRequest\x1a\x1e.cln.PreapprovekeysendResponse\"\x00\x12T\n\x11PreApproveInvoice\x12\x1d.cln.PreapproveinvoiceRequest\x1a\x1e.cln.PreapproveinvoiceResponse\"\x00\x12\x45\n\x0cStaticBackup\x12\x18.cln.StaticbackupRequest\x1a\x19.cln.StaticbackupResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xc1\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x61lias\x18\x02 \x01(\tH\x00\x88\x01\x01\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\x33\n\x0cour_features\x18\n \x01(\x0b\x32\x18.cln.GetinfoOur_featuresH\x01\x88\x01\x01\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\x02\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_aliasB\x0f\n\r_our_featuresB\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\"\xc4\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\"G\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\x42\n\n\x08_address\"\x8a\x02\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\"_\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\r\n\tWEBSOCKET\x10\x05\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\"\x8e\x02\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x00\x88\x01\x01\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\x01\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x42\x0f\n\r_num_channelsB\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\"\x95\x18\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\x38\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\n\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0c\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\r\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12\x38\n\x07\x66unding\x18\x13 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH \x88\x01\x01\x12\x34\n\x05\x61lias\x18\x32 \x01(\x0b\x32 .cln.ListpeersPeersChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\"\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\"\xe0\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\n\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0b\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\x0c\x42\x0f\n\r_scratch_txidB\n\n\x08_feerateB\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\n\n\x08_fundingB\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\x08\n\x06_aliasB\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\"\xf3\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\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x00\x88\x01\x01\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\x42\x10\n\x0e_splice_amount\"\x9b\x02\n\x1dListpeersPeersChannelsFunding\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\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\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\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\"\xf1\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\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"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\"\x83\x03\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\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\xab\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\x17\n\nchannel_id\x18\t \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x01\x88\x01\x01\x42\r\n\x0b_channel_idB\x13\n\x11_short_channel_id\"\xdd\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\x1a\n\rlocalinvreqid\x18\x0b \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\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xad\x05\n\x0fSendpayResponse\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x01\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x02\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\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x05\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\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x0b\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\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\"\xb3\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\x11\n\tdirection\x18\x10 \x01(\r\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\"\xb4\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\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\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\"\xfe\x05\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\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12;\n\rpaid_outpoint\x18\x11 \x01(\x0b\x32\x1f.cln.CreateinvoicePaid_outpointH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\t\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\n\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\x10\n\x0e_created_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"X\n\x1a\x43reateinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\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\"\x17\n\x15\x44\x61tastoreusageRequest\"k\n\x16\x44\x61tastoreusageResponse\x12>\n\x0e\x64\x61tastoreusage\x18\x01 \x01(\x0b\x32!.cln.DatastoreusageDatastoreusageH\x00\x88\x01\x01\x42\x11\n\x0f_datastoreusage\"b\n\x1c\x44\x61tastoreusageDatastoreusage\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0btotal_bytes\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x06\n\x04_keyB\x0e\n\x0c_total_bytes\"\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\"\xa1\x04\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\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x04\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x05\x88\x01\x01\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\x06\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x07\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\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xfa\x01\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\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\x95\x03\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\x1a\n\rcreated_index\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x03\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x05\x88\x01\x01\x42\x10\n\x0e_created_indexB\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\"\xde\x02\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\x12>\n\x05index\x18\x05 \x01(\x0e\x32*.cln.ListinvoicesRequest.ListinvoicesIndexH\x04\x88\x01\x01\x12\x12\n\x05start\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x12\n\x05limit\x18\x07 \x01(\rH\x06\x88\x01\x01\"-\n\x11ListinvoicesIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xd4\x06\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\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x07\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x08\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\t\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\n\x88\x01\x01\x12\x42\n\rpaid_outpoint\x18\x12 \x01(\x0b\x32&.cln.ListinvoicesInvoicesPaid_outpointH\x0b\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x0c\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\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"_\n!ListinvoicesInvoicesPaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\x8a\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12*\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x17.cln.SendonionFirst_hop\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\x1a\n\rlocalinvreqid\x18\r \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\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xe7\x04\n\x11SendonionResponse\x12\x1a\n\rcreated_index\x18\x0e \x01(\x04H\x00\x88\x01\x01\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\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x02\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\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0f \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\t\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\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\x10\n\x0e_updated_indexB\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\"\xa0\x03\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\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListsendpaysRequest.ListsendpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\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\"-\n\x11ListsendpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd0\x05\n\x14ListsendpaysPayments\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x02\x88\x01\x01\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\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\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\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\n\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\x10\n\x0e_created_indexB\t\n\x07_partidB\x10\n\x0e_updated_indexB\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\"\xf8\x01\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\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.ListtransactionsTransactionsOutputs\"S\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\"l\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\"\xda\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\x1a\n\rlocalinvreqid\x18\x0e \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\x10\n\x0e_localinvreqidB\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\"\xe8\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\"P\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\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\"\xbf\x05\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\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12<\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32 .cln.WaitanyinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\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\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"Y\n\x1bWaitanyinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xb0\x05\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\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12\x39\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1d.cln.WaitinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\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\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"V\n\x18WaitinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\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\"\x8e\x05\n\x13WaitsendpayResponse\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x01\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\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\x04\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x05\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\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x10\n\x0e_created_indexB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_updated_indexB\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\"\x97\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\"3\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x08\n\x04P2TR\x10\x03\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"M\n\x0fNewaddrResponse\x12\x11\n\x04p2tr\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_p2trB\t\n\x07_bech32\"\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\"\x82\x03\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\x12&\n\textratlvs\x18\t \x01(\x0b\x32\x0e.cln.TlvStreamH\x06\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_routehintsB\x0c\n\n_extratlvs\"\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\"\xa4\x03\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\x12\x17\n\nnonwrapped\x18\t \x01(\x08H\x05\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x06\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_changeB\r\n\x0b_nonwrappedB\x19\n\x17_opening_anchor_channel\"\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\"\x9b\x03\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\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x05\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_changeB\x19\n\x17_opening_anchor_channel\"\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\"1\n\x17ListpeerchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"K\n\x18ListpeerchannelsResponse\x12/\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1d.cln.ListpeerchannelsChannels\"\xe2\x1a\n\x18ListpeerchannelsChannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0epeer_connected\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12O\n\x05state\x18\x03 \x01(\x0e\x32;.cln.ListpeerchannelsChannels.ListpeerchannelsChannelsStateH\x02\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x04 \x01(\x0cH\x03\x88\x01\x01\x12:\n\x07updates\x18\x37 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsUpdatesH\x04\x88\x01\x01\x12\x1e\n\x11ignore_fee_limits\x18\x36 \x01(\x08H\x05\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x06\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x08\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\t\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\n\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\x0b\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\x0c\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\r\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\x0e\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\x0f\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x10\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x11\x88\x01\x01\x12%\n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSideH\x12\x88\x01\x01\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\x13\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x14\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x1a\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1e\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x1f\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18# \x01(\x0b\x32\x0b.cln.AmountH!\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18$ \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18% \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12 \n\x13their_to_self_delay\x18& \x01(\rH$\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\' \x01(\rH%\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18( \x01(\rH&\x88\x01\x01\x12\x36\n\x05\x61lias\x18) \x01(\x0b\x32\".cln.ListpeerchannelsChannelsAliasH\'\x88\x01\x01\x12\x0e\n\x06status\x18+ \x03(\t\x12 \n\x13in_payments_offered\x18, \x01(\x04H(\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\x30 \x01(\x04H,\x88\x01\x01\x12*\n\x10out_offered_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH-\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18\x32 \x01(\x04H.\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18\x33 \x01(\x0b\x32\x0b.cln.AmountH/\x88\x01\x01\x12#\n\x16last_stable_connection\x18\x38 \x01(\x04H0\x88\x01\x01\x12\x31\n\x05htlcs\x18\x34 \x03(\x0b\x32\".cln.ListpeerchannelsChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18\x35 \x01(\tH1\x88\x01\x01\"\x80\x03\n\x1dListpeerchannelsChannelsState\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\n\x12\x1c\n\x18\x43HANNELD_AWAITING_SPLICE\x10\x0b\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0c\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\rB\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\n\n\x08_updatesB\x14\n\x12_ignore_fee_limitsB\n\n\x08_feerateB\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_openerB\t\n\x07_closerB\n\n\x08_fundingB\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\x08\n\x06_aliasB\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\x19\n\x17_last_stable_connectionB\x10\n\x0e_close_to_addr\"\xb6\x01\n\x1fListpeerchannelsChannelsUpdates\x12=\n\x05local\x18\x01 \x01(\x0b\x32).cln.ListpeerchannelsChannelsUpdatesLocalH\x00\x88\x01\x01\x12?\n\x06remote\x18\x02 \x01(\x0b\x32*.cln.ListpeerchannelsChannelsUpdatesRemoteH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xe7\x02\n$ListpeerchannelsChannelsUpdatesLocal\x12+\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x14\n\x12_cltv_expiry_deltaB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionths\"\xe8\x02\n%ListpeerchannelsChannelsUpdatesRemote\x12+\n\x11htlc_minimum_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12+\n\x11htlc_maximum_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x1e\n\x11\x63ltv_expiry_delta\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x14\n\x12_htlc_minimum_msatB\x14\n\x12_htlc_maximum_msatB\x14\n\x12_cltv_expiry_deltaB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionths\"]\n\x1fListpeerchannelsChannelsFeerate\x12\x12\n\x05perkw\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05perkb\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x08\n\x06_perkwB\x08\n\x06_perkb\"\x80\x03\n ListpeerchannelsChannelsInflight\x12\x19\n\x0c\x66unding_txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x03 \x01(\tH\x02\x88\x01\x01\x12,\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x04\x88\x01\x01\x12*\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x06 \x01(\x0cH\x06\x88\x01\x01\x42\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\n\n\x08_feerateB\x15\n\x13_total_funding_msatB\x10\n\x0e_splice_amountB\x13\n\x11_our_funding_msatB\x0f\n\r_scratch_txid\"\xd2\x02\n\x1fListpeerchannelsChannelsFunding\x12%\n\x0bpushed_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12*\n\x10local_funds_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11remote_funds_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x13\n\x11_local_funds_msatB\x14\n\x12_remote_funds_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"]\n\x1dListpeerchannelsChannelsAlias\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\"\xe2\x03\n\x1dListpeerchannelsChannelsHtlcs\x12\x61\n\tdirection\x18\x01 \x01(\x0e\x32I.cln.ListpeerchannelsChannelsHtlcs.ListpeerchannelsChannelsHtlcsDirectionH\x00\x88\x01\x01\x12\x0f\n\x02id\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x05 \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\"\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcStateH\x07\x88\x01\x01\"9\n&ListpeerchannelsChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x0c\n\n_directionB\x05\n\x03_idB\x0e\n\x0c_amount_msatB\t\n\x07_expiryB\x0f\n\r_payment_hashB\x10\n\x0e_local_trimmedB\t\n\x07_statusB\x08\n\x06_state\"3\n\x19ListclosedchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"[\n\x1aListclosedchannelsResponse\x12=\n\x0e\x63losedchannels\x18\x01 \x03(\x0b\x32%.cln.ListclosedchannelsClosedchannels\"\xf2\t\n ListclosedchannelsClosedchannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x12>\n\x05\x61lias\x18\x04 \x01(\x0b\x32*.cln.ListclosedchannelsClosedchannelsAliasH\x02\x88\x01\x01\x12 \n\x06opener\x18\x05 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x06 \x01(\x0e\x32\x10.cln.ChannelSideH\x03\x88\x01\x01\x12\x0f\n\x07private\x18\x07 \x01(\x08\x12\x1f\n\x17total_local_commitments\x18\t \x01(\x04\x12 \n\x18total_remote_commitments\x18\n \x01(\x04\x12\x18\n\x10total_htlcs_sent\x18\x0b \x01(\x04\x12\x14\n\x0c\x66unding_txid\x18\x0c \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\r \x01(\r\x12\x0e\n\x06leased\x18\x0e \x01(\x08\x12/\n\x15\x66unding_fee_paid_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12/\n\x15\x66unding_fee_rcvd_msat\x18\x10 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12-\n\x13\x66unding_pushed_msat\x18\x11 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1f\n\ntotal_msat\x18\x12 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x66inal_to_us_msat\x18\x13 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emin_to_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emax_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14last_commitment_txid\x18\x16 \x01(\x0cH\x07\x88\x01\x01\x12\x32\n\x18last_commitment_fee_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x66\n\x0b\x63lose_cause\x18\x18 \x01(\x0e\x32Q.cln.ListclosedchannelsClosedchannels.ListclosedchannelsClosedchannelsClose_cause\x12#\n\x16last_stable_connection\x18\x19 \x01(\x04H\t\x88\x01\x01\"v\n+ListclosedchannelsClosedchannelsClose_cause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\n\n\x08_peer_idB\x13\n\x11_short_channel_idB\x08\n\x06_aliasB\t\n\x07_closerB\x18\n\x16_funding_fee_paid_msatB\x18\n\x16_funding_fee_rcvd_msatB\x16\n\x14_funding_pushed_msatB\x17\n\x15_last_commitment_txidB\x1b\n\x19_last_commitment_fee_msatB\x19\n\x17_last_stable_connection\"e\n%ListclosedchannelsClosedchannelsAlias\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\"L\n\x10\x44\x65\x63odepayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x04\n\x11\x44\x65\x63odepayResponse\x12\x10\n\x08\x63urrency\x18\x01 \x01(\t\x12\x12\n\ncreated_at\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\x04\x12\r\n\x05payee\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x11\n\tsignature\x18\x07 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x1d\n\x15min_final_cltv_expiry\x18\n \x01(\r\x12\x1b\n\x0epayment_secret\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\r \x01(\x0cH\x05\x88\x01\x01\x12*\n\tfallbacks\x18\x0e \x03(\x0b\x32\x17.cln.DecodepayFallbacks\x12\"\n\x05\x65xtra\x18\x10 \x03(\x0b\x32\x13.cln.DecodepayExtraB\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x13\n\x11_description_hashB\x11\n\x0f_payment_secretB\x0b\n\t_featuresB\x13\n\x11_payment_metadata\"\xd0\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"N\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\xe8!\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12+\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x16.cln.DecodeOffer_paths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x37\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1c.cln.DecodeInvoice_fallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\x12\x16\n\tdecrypted\x18L \x01(\x0cH=\x88\x01\x01\"\x83\x01\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x12\x15\n\x11\x45MERGENCY_RECOVER\x10\x05\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hexB\x0c\n\n_decrypted\"<\n\x11\x44\x65\x63odeOffer_paths\x12\x15\n\rfirst_node_id\x18\x01 \x01(\x0c\x12\x10\n\x08\x62linding\x18\x02 \x01(\x0c\"\x8a\x01\n\x1f\x44\x65\x63odeOffer_recurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"T\n\x17\x44\x65\x63odeInvoice_pathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"Y\n\x17\x44\x65\x63odeInvoice_fallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"w\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x42,\n*_warning_invoice_fallbacks_version_invalid\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"=\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\"\x9c\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x46\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32\".cln.FeeratesOnchain_fee_estimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\xd3\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\xd3\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\x9b\x02\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\x30\n#unilateral_close_nonanchor_satoshis\x18\x06 \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\x42&\n$_unilateral_close_nonanchor_satoshis\"\xe9\x02\n\x13\x46\x65tchinvoiceRequest\x12\r\n\x05offer\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x15\n\x08quantity\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x1f\n\x12recurrence_counter\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x10recurrence_start\x18\x05 \x01(\x01H\x03\x88\x01\x01\x12\x1d\n\x10recurrence_label\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07timeout\x18\x07 \x01(\x01H\x05\x88\x01\x01\x12\x17\n\npayer_note\x18\x08 \x01(\tH\x06\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x0b\n\t_quantityB\x15\n\x13_recurrence_counterB\x13\n\x11_recurrence_startB\x13\n\x11_recurrence_labelB\n\n\x08_timeoutB\r\n\x0b_payer_note\"\x9a\x01\n\x14\x46\x65tchinvoiceResponse\x12\x0f\n\x07invoice\x18\x01 \x01(\t\x12)\n\x07\x63hanges\x18\x02 \x01(\x0b\x32\x18.cln.FetchinvoiceChanges\x12\x36\n\x0bnext_period\x18\x03 \x01(\x0b\x32\x1c.cln.FetchinvoiceNext_periodH\x00\x88\x01\x01\x42\x0e\n\x0c_next_period\"\x82\x02\n\x13\x46\x65tchinvoiceChanges\x12!\n\x14\x64\x65scription_appended\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0evendor_removed\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06vendor\x18\x04 \x01(\tH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x17\n\x15_description_appendedB\x0e\n\x0c_descriptionB\x11\n\x0f_vendor_removedB\t\n\x07_vendorB\x0e\n\x0c_amount_msat\"~\n\x17\x46\x65tchinvoiceNext_period\x12\x0f\n\x07\x63ounter\x18\x01 \x01(\x04\x12\x11\n\tstarttime\x18\x02 \x01(\x04\x12\x0f\n\x07\x65ndtime\x18\x03 \x01(\x04\x12\x17\n\x0fpaywindow_start\x18\x04 \x01(\x04\x12\x15\n\rpaywindow_end\x18\x05 \x01(\x04\"\xfb\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\x12\x14\n\x0c\x63hannel_type\x18\x0e \x03(\rB\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\"\xe5\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\x37\n\x0c\x63hannel_type\x18\x07 \x01(\x0b\x32\x1c.cln.FundchannelChannel_typeH\x00\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x02\x88\x01\x01\x42\x0f\n\r_channel_typeB\x0b\n\t_close_toB\x0b\n\t_mindepth\"L\n\x17\x46undchannelChannel_type\x12\x0c\n\x04\x62its\x18\x01 \x03(\r\x12#\n\x05names\x18\x02 \x03(\x0e\x32\x14.cln.ChannelTypeName\"\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(\rH\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\"\xb7\x03\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\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListforwardsRequest.ListforwardsIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\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\"-\n\x11ListforwardsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channelB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xba\x05\n\x14ListforwardsForwards\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x00\x88\x01\x01\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x01\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\x02\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x04\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x05\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x07\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\x10\n\x0e_created_indexB\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x10\n\x0e_updated_indexB\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\"\xff\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%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\t\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\n\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\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"*\n\x10ListhtlcsRequest\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListhtlcsResponse\x12\"\n\x05htlcs\x18\x01 \x03(\x0b\x32\x13.cln.ListhtlcsHtlcs\"\x89\x02\n\x0eListhtlcsHtlcs\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12>\n\tdirection\x18\x05 \x01(\x0e\x32+.cln.ListhtlcsHtlcs.ListhtlcsHtlcsDirection\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1d\n\x05state\x18\x07 \x01(\x0e\x32\x0e.cln.HtlcState\"*\n\x17ListhtlcsHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xaa\x02\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\x12\x1c\n\x0fignorefeelimits\x18\x07 \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelayB\x12\n\x10_ignorefeelimits\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\xca\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\x1e\n\x11ignore_fee_limits\x18\n \x01(\x08H\x01\x88\x01\x01\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x02\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\x03\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x14\n\x12_ignore_fee_limitsB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\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\"O\n\x16WaitblockheightRequest\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\x12\x14\n\x07timeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_timeout\".\n\x17WaitblockheightResponse\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\"\xf9\x01\n\x0bWaitRequest\x12\x31\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitSubsystem\x12\x31\n\tindexname\x18\x02 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitIndexname\x12\x11\n\tnextvalue\x18\x03 \x01(\x04\"9\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\"6\n\rWaitIndexname\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x12\x0b\n\x07\x44\x45LETED\x10\x02\"\xe3\x01\n\x0cWaitResponse\x12\x32\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1f.cln.WaitResponse.WaitSubsystem\x12\x14\n\x07\x63reated\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07updated\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07\x64\x65leted\x18\x04 \x01(\x04H\x02\x88\x01\x01\"9\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\x42\n\n\x08_createdB\n\n\x08_updatedB\n\n\x08_deleted\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse\"\xa7\x01\n\x18PreapprovekeysendRequest\x12\x18\n\x0b\x64\x65stination\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_destinationB\x0f\n\r_payment_hashB\x0e\n\x0c_amount_msat\"\x1b\n\x19PreapprovekeysendResponse\":\n\x18PreapproveinvoiceRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_bolt11\"\x1b\n\x19PreapproveinvoiceResponse\"\x15\n\x13StaticbackupRequest\"#\n\x14StaticbackupResponse\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\x32\xa2\x1f\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\x12K\n\x0e\x44\x61tastoreUsage\x12\x1a.cln.DatastoreusageRequest\x1a\x1b.cln.DatastoreusageResponse\"\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\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\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\x45\n\x0c\x46\x65tchInvoice\x12\x18.cln.FetchinvoiceRequest\x1a\x19.cln.FetchinvoiceResponse\"\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\tListHtlcs\x12\x15.cln.ListhtlcsRequest\x1a\x16.cln.ListhtlcsResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12N\n\x0fWaitBlockHeight\x12\x1b.cln.WaitblockheightRequest\x1a\x1c.cln.WaitblockheightResponse\"\x00\x12-\n\x04Wait\x12\x10.cln.WaitRequest\x1a\x11.cln.WaitResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x12T\n\x11PreApproveKeysend\x12\x1d.cln.PreapprovekeysendRequest\x1a\x1e.cln.PreapprovekeysendResponse\"\x00\x12T\n\x11PreApproveInvoice\x12\x1d.cln.PreapproveinvoiceRequest\x1a\x1e.cln.PreapproveinvoiceResponse\"\x00\x12\x45\n\x0cStaticBackup\x12\x18.cln.StaticbackupRequest\x1a\x19.cln.StaticbackupResponse\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -298,193 +298,193 @@ _globals['_LISTPEERCHANNELSRESPONSE']._serialized_start=25722 _globals['_LISTPEERCHANNELSRESPONSE']._serialized_end=25797 _globals['_LISTPEERCHANNELSCHANNELS']._serialized_start=25800 - _globals['_LISTPEERCHANNELSCHANNELS']._serialized_end=29162 - _globals['_LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE']._serialized_start=27851 - _globals['_LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE']._serialized_end=28235 - _globals['_LISTPEERCHANNELSCHANNELSUPDATES']._serialized_start=29165 - _globals['_LISTPEERCHANNELSCHANNELSUPDATES']._serialized_end=29347 - _globals['_LISTPEERCHANNELSCHANNELSUPDATESLOCAL']._serialized_start=29350 - _globals['_LISTPEERCHANNELSCHANNELSUPDATESLOCAL']._serialized_end=29709 - _globals['_LISTPEERCHANNELSCHANNELSUPDATESREMOTE']._serialized_start=29712 - _globals['_LISTPEERCHANNELSCHANNELSUPDATESREMOTE']._serialized_end=30072 - _globals['_LISTPEERCHANNELSCHANNELSFEERATE']._serialized_start=30074 - _globals['_LISTPEERCHANNELSCHANNELSFEERATE']._serialized_end=30167 - _globals['_LISTPEERCHANNELSCHANNELSINFLIGHT']._serialized_start=30170 - _globals['_LISTPEERCHANNELSCHANNELSINFLIGHT']._serialized_end=30554 - _globals['_LISTPEERCHANNELSCHANNELSFUNDING']._serialized_start=30557 - _globals['_LISTPEERCHANNELSCHANNELSFUNDING']._serialized_end=30895 - _globals['_LISTPEERCHANNELSCHANNELSALIAS']._serialized_start=30897 - _globals['_LISTPEERCHANNELSCHANNELSALIAS']._serialized_end=30990 - _globals['_LISTPEERCHANNELSCHANNELSHTLCS']._serialized_start=30993 - _globals['_LISTPEERCHANNELSCHANNELSHTLCS']._serialized_end=31475 - _globals['_LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION']._serialized_start=31314 - _globals['_LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION']._serialized_end=31371 - _globals['_LISTCLOSEDCHANNELSREQUEST']._serialized_start=31477 - _globals['_LISTCLOSEDCHANNELSREQUEST']._serialized_end=31528 - _globals['_LISTCLOSEDCHANNELSRESPONSE']._serialized_start=31530 - _globals['_LISTCLOSEDCHANNELSRESPONSE']._serialized_end=31621 - _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS']._serialized_start=31624 - _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS']._serialized_end=32826 - _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE']._serialized_start=32524 - _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE']._serialized_end=32642 - _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS']._serialized_start=32828 - _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS']._serialized_end=32929 - _globals['_DECODEPAYREQUEST']._serialized_start=32931 - _globals['_DECODEPAYREQUEST']._serialized_end=33007 - _globals['_DECODEPAYRESPONSE']._serialized_start=33010 - _globals['_DECODEPAYRESPONSE']._serialized_end=33535 - _globals['_DECODEPAYFALLBACKS']._serialized_start=33538 - _globals['_DECODEPAYFALLBACKS']._serialized_end=33746 - _globals['_DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE']._serialized_start=33659 - _globals['_DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE']._serialized_end=33737 - _globals['_DECODEPAYEXTRA']._serialized_start=33748 - _globals['_DECODEPAYEXTRA']._serialized_end=33791 - _globals['_DECODEREQUEST']._serialized_start=33793 - _globals['_DECODEREQUEST']._serialized_end=33824 - _globals['_DECODERESPONSE']._serialized_start=33827 - _globals['_DECODERESPONSE']._serialized_end=38155 - _globals['_DECODERESPONSE_DECODETYPE']._serialized_start=36420 - _globals['_DECODERESPONSE_DECODETYPE']._serialized_end=36551 - _globals['_DECODEOFFER_PATHS']._serialized_start=38157 - _globals['_DECODEOFFER_PATHS']._serialized_end=38217 - _globals['_DECODEOFFER_RECURRENCEPAYWINDOW']._serialized_start=38220 - _globals['_DECODEOFFER_RECURRENCEPAYWINDOW']._serialized_end=38358 - _globals['_DECODEINVOICE_PATHSPATH']._serialized_start=38360 - _globals['_DECODEINVOICE_PATHSPATH']._serialized_end=38444 - _globals['_DECODEINVOICE_FALLBACKS']._serialized_start=38446 - _globals['_DECODEINVOICE_FALLBACKS']._serialized_end=38535 - _globals['_DECODEFALLBACKS']._serialized_start=38537 - _globals['_DECODEFALLBACKS']._serialized_end=38656 - _globals['_DECODEEXTRA']._serialized_start=38658 - _globals['_DECODEEXTRA']._serialized_end=38698 - _globals['_DECODERESTRICTIONS']._serialized_start=38700 - _globals['_DECODERESTRICTIONS']._serialized_end=38759 - _globals['_DISCONNECTREQUEST']._serialized_start=38761 - _globals['_DISCONNECTREQUEST']._serialized_end=38822 - _globals['_DISCONNECTRESPONSE']._serialized_start=38824 - _globals['_DISCONNECTRESPONSE']._serialized_end=38844 - _globals['_FEERATESREQUEST']._serialized_start=38846 - _globals['_FEERATESREQUEST']._serialized_end=38953 - _globals['_FEERATESREQUEST_FEERATESSTYLE']._serialized_start=38916 - _globals['_FEERATESREQUEST_FEERATESSTYLE']._serialized_end=38953 - _globals['_FEERATESRESPONSE']._serialized_start=38956 - _globals['_FEERATESRESPONSE']._serialized_end=39240 - _globals['_FEERATESPERKB']._serialized_start=39243 - _globals['_FEERATESPERKB']._serialized_end=39710 - _globals['_FEERATESPERKBESTIMATES']._serialized_start=39713 - _globals['_FEERATESPERKBESTIMATES']._serialized_end=39863 - _globals['_FEERATESPERKW']._serialized_start=39866 - _globals['_FEERATESPERKW']._serialized_end=40333 - _globals['_FEERATESPERKWESTIMATES']._serialized_start=40336 - _globals['_FEERATESPERKWESTIMATES']._serialized_end=40486 - _globals['_FEERATESONCHAIN_FEE_ESTIMATES']._serialized_start=40489 - _globals['_FEERATESONCHAIN_FEE_ESTIMATES']._serialized_end=40772 - _globals['_FETCHINVOICEREQUEST']._serialized_start=40775 - _globals['_FETCHINVOICEREQUEST']._serialized_end=41136 - _globals['_FETCHINVOICERESPONSE']._serialized_start=41139 - _globals['_FETCHINVOICERESPONSE']._serialized_end=41293 - _globals['_FETCHINVOICECHANGES']._serialized_start=41296 - _globals['_FETCHINVOICECHANGES']._serialized_end=41554 - _globals['_FETCHINVOICENEXT_PERIOD']._serialized_start=41556 - _globals['_FETCHINVOICENEXT_PERIOD']._serialized_end=41682 - _globals['_FUNDCHANNELREQUEST']._serialized_start=41685 - _globals['_FUNDCHANNELREQUEST']._serialized_end=42192 - _globals['_FUNDCHANNELRESPONSE']._serialized_start=42195 - _globals['_FUNDCHANNELRESPONSE']._serialized_end=42424 - _globals['_FUNDCHANNELCHANNEL_TYPE']._serialized_start=42426 - _globals['_FUNDCHANNELCHANNEL_TYPE']._serialized_end=42502 - _globals['_GETROUTEREQUEST']._serialized_start=42505 - _globals['_GETROUTEREQUEST']._serialized_end=42741 - _globals['_GETROUTERESPONSE']._serialized_start=42743 - _globals['_GETROUTERESPONSE']._serialized_end=42796 - _globals['_GETROUTEROUTE']._serialized_start=42799 - _globals['_GETROUTEROUTE']._serialized_end=42996 - _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_start=42967 - _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_end=42996 - _globals['_LISTFORWARDSREQUEST']._serialized_start=42999 - _globals['_LISTFORWARDSREQUEST']._serialized_end=43438 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_start=43243 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_end=43319 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_start=43321 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_end=43366 - _globals['_LISTFORWARDSRESPONSE']._serialized_start=43440 - _globals['_LISTFORWARDSRESPONSE']._serialized_end=43507 - _globals['_LISTFORWARDSFORWARDS']._serialized_start=43510 - _globals['_LISTFORWARDSFORWARDS']._serialized_end=44208 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_start=43955 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_end=44039 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_start=44041 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_end=44089 - _globals['_LISTPAYSREQUEST']._serialized_start=44211 - _globals['_LISTPAYSREQUEST']._serialized_end=44430 - _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_start=44336 - _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_end=44391 - _globals['_LISTPAYSRESPONSE']._serialized_start=44432 - _globals['_LISTPAYSRESPONSE']._serialized_end=44483 - _globals['_LISTPAYSPAYS']._serialized_start=44486 - _globals['_LISTPAYSPAYS']._serialized_end=45125 - _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_start=44900 - _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_end=44959 - _globals['_LISTHTLCSREQUEST']._serialized_start=45127 - _globals['_LISTHTLCSREQUEST']._serialized_end=45169 - _globals['_LISTHTLCSRESPONSE']._serialized_start=45171 - _globals['_LISTHTLCSRESPONSE']._serialized_end=45226 - _globals['_LISTHTLCSHTLCS']._serialized_start=45229 - _globals['_LISTHTLCSHTLCS']._serialized_end=45494 - _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_start=45452 - _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_end=45494 - _globals['_PINGREQUEST']._serialized_start=45496 - _globals['_PINGREQUEST']._serialized_end=45585 - _globals['_PINGRESPONSE']._serialized_start=45587 - _globals['_PINGRESPONSE']._serialized_end=45617 - _globals['_SENDCUSTOMMSGREQUEST']._serialized_start=45619 - _globals['_SENDCUSTOMMSGREQUEST']._serialized_end=45671 - _globals['_SENDCUSTOMMSGRESPONSE']._serialized_start=45673 - _globals['_SENDCUSTOMMSGRESPONSE']._serialized_end=45712 - _globals['_SETCHANNELREQUEST']._serialized_start=45715 - _globals['_SETCHANNELREQUEST']._serialized_end=46013 - _globals['_SETCHANNELRESPONSE']._serialized_start=46015 - _globals['_SETCHANNELRESPONSE']._serialized_end=46078 - _globals['_SETCHANNELCHANNELS']._serialized_start=46081 - _globals['_SETCHANNELCHANNELS']._serialized_end=46539 - _globals['_SIGNINVOICEREQUEST']._serialized_start=46541 - _globals['_SIGNINVOICEREQUEST']._serialized_end=46580 - _globals['_SIGNINVOICERESPONSE']._serialized_start=46582 - _globals['_SIGNINVOICERESPONSE']._serialized_end=46619 - _globals['_SIGNMESSAGEREQUEST']._serialized_start=46621 - _globals['_SIGNMESSAGEREQUEST']._serialized_end=46658 - _globals['_SIGNMESSAGERESPONSE']._serialized_start=46660 - _globals['_SIGNMESSAGERESPONSE']._serialized_end=46730 - _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_start=46732 - _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_end=46811 - _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_start=46813 - _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_end=46859 - _globals['_WAITREQUEST']._serialized_start=46862 - _globals['_WAITREQUEST']._serialized_end=47111 - _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_start=46998 - _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_end=47055 - _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_start=47057 - _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_end=47111 - _globals['_WAITRESPONSE']._serialized_start=47114 - _globals['_WAITRESPONSE']._serialized_end=47341 - _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_start=46998 - _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_end=47055 - _globals['_STOPREQUEST']._serialized_start=47343 - _globals['_STOPREQUEST']._serialized_end=47356 - _globals['_STOPRESPONSE']._serialized_start=47358 - _globals['_STOPRESPONSE']._serialized_end=47372 - _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_start=47375 - _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_end=47542 - _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_start=47544 - _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_end=47571 - _globals['_PREAPPROVEINVOICEREQUEST']._serialized_start=47573 - _globals['_PREAPPROVEINVOICEREQUEST']._serialized_end=47631 - _globals['_PREAPPROVEINVOICERESPONSE']._serialized_start=47633 - _globals['_PREAPPROVEINVOICERESPONSE']._serialized_end=47660 - _globals['_STATICBACKUPREQUEST']._serialized_start=47662 - _globals['_STATICBACKUPREQUEST']._serialized_end=47683 - _globals['_STATICBACKUPRESPONSE']._serialized_start=47685 - _globals['_STATICBACKUPRESPONSE']._serialized_end=47720 - _globals['_NODE']._serialized_start=47723 - _globals['_NODE']._serialized_end=51725 + _globals['_LISTPEERCHANNELSCHANNELS']._serialized_end=29226 + _globals['_LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE']._serialized_start=27888 + _globals['_LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE']._serialized_end=28272 + _globals['_LISTPEERCHANNELSCHANNELSUPDATES']._serialized_start=29229 + _globals['_LISTPEERCHANNELSCHANNELSUPDATES']._serialized_end=29411 + _globals['_LISTPEERCHANNELSCHANNELSUPDATESLOCAL']._serialized_start=29414 + _globals['_LISTPEERCHANNELSCHANNELSUPDATESLOCAL']._serialized_end=29773 + _globals['_LISTPEERCHANNELSCHANNELSUPDATESREMOTE']._serialized_start=29776 + _globals['_LISTPEERCHANNELSCHANNELSUPDATESREMOTE']._serialized_end=30136 + _globals['_LISTPEERCHANNELSCHANNELSFEERATE']._serialized_start=30138 + _globals['_LISTPEERCHANNELSCHANNELSFEERATE']._serialized_end=30231 + _globals['_LISTPEERCHANNELSCHANNELSINFLIGHT']._serialized_start=30234 + _globals['_LISTPEERCHANNELSCHANNELSINFLIGHT']._serialized_end=30618 + _globals['_LISTPEERCHANNELSCHANNELSFUNDING']._serialized_start=30621 + _globals['_LISTPEERCHANNELSCHANNELSFUNDING']._serialized_end=30959 + _globals['_LISTPEERCHANNELSCHANNELSALIAS']._serialized_start=30961 + _globals['_LISTPEERCHANNELSCHANNELSALIAS']._serialized_end=31054 + _globals['_LISTPEERCHANNELSCHANNELSHTLCS']._serialized_start=31057 + _globals['_LISTPEERCHANNELSCHANNELSHTLCS']._serialized_end=31539 + _globals['_LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION']._serialized_start=31378 + _globals['_LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION']._serialized_end=31435 + _globals['_LISTCLOSEDCHANNELSREQUEST']._serialized_start=31541 + _globals['_LISTCLOSEDCHANNELSREQUEST']._serialized_end=31592 + _globals['_LISTCLOSEDCHANNELSRESPONSE']._serialized_start=31594 + _globals['_LISTCLOSEDCHANNELSRESPONSE']._serialized_end=31685 + _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS']._serialized_start=31688 + _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS']._serialized_end=32954 + _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE']._serialized_start=32625 + _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE']._serialized_end=32743 + _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS']._serialized_start=32956 + _globals['_LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS']._serialized_end=33057 + _globals['_DECODEPAYREQUEST']._serialized_start=33059 + _globals['_DECODEPAYREQUEST']._serialized_end=33135 + _globals['_DECODEPAYRESPONSE']._serialized_start=33138 + _globals['_DECODEPAYRESPONSE']._serialized_end=33663 + _globals['_DECODEPAYFALLBACKS']._serialized_start=33666 + _globals['_DECODEPAYFALLBACKS']._serialized_end=33874 + _globals['_DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE']._serialized_start=33787 + _globals['_DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE']._serialized_end=33865 + _globals['_DECODEPAYEXTRA']._serialized_start=33876 + _globals['_DECODEPAYEXTRA']._serialized_end=33919 + _globals['_DECODEREQUEST']._serialized_start=33921 + _globals['_DECODEREQUEST']._serialized_end=33952 + _globals['_DECODERESPONSE']._serialized_start=33955 + _globals['_DECODERESPONSE']._serialized_end=38283 + _globals['_DECODERESPONSE_DECODETYPE']._serialized_start=36548 + _globals['_DECODERESPONSE_DECODETYPE']._serialized_end=36679 + _globals['_DECODEOFFER_PATHS']._serialized_start=38285 + _globals['_DECODEOFFER_PATHS']._serialized_end=38345 + _globals['_DECODEOFFER_RECURRENCEPAYWINDOW']._serialized_start=38348 + _globals['_DECODEOFFER_RECURRENCEPAYWINDOW']._serialized_end=38486 + _globals['_DECODEINVOICE_PATHSPATH']._serialized_start=38488 + _globals['_DECODEINVOICE_PATHSPATH']._serialized_end=38572 + _globals['_DECODEINVOICE_FALLBACKS']._serialized_start=38574 + _globals['_DECODEINVOICE_FALLBACKS']._serialized_end=38663 + _globals['_DECODEFALLBACKS']._serialized_start=38665 + _globals['_DECODEFALLBACKS']._serialized_end=38784 + _globals['_DECODEEXTRA']._serialized_start=38786 + _globals['_DECODEEXTRA']._serialized_end=38826 + _globals['_DECODERESTRICTIONS']._serialized_start=38828 + _globals['_DECODERESTRICTIONS']._serialized_end=38887 + _globals['_DISCONNECTREQUEST']._serialized_start=38889 + _globals['_DISCONNECTREQUEST']._serialized_end=38950 + _globals['_DISCONNECTRESPONSE']._serialized_start=38952 + _globals['_DISCONNECTRESPONSE']._serialized_end=38972 + _globals['_FEERATESREQUEST']._serialized_start=38974 + _globals['_FEERATESREQUEST']._serialized_end=39081 + _globals['_FEERATESREQUEST_FEERATESSTYLE']._serialized_start=39044 + _globals['_FEERATESREQUEST_FEERATESSTYLE']._serialized_end=39081 + _globals['_FEERATESRESPONSE']._serialized_start=39084 + _globals['_FEERATESRESPONSE']._serialized_end=39368 + _globals['_FEERATESPERKB']._serialized_start=39371 + _globals['_FEERATESPERKB']._serialized_end=39838 + _globals['_FEERATESPERKBESTIMATES']._serialized_start=39841 + _globals['_FEERATESPERKBESTIMATES']._serialized_end=39991 + _globals['_FEERATESPERKW']._serialized_start=39994 + _globals['_FEERATESPERKW']._serialized_end=40461 + _globals['_FEERATESPERKWESTIMATES']._serialized_start=40464 + _globals['_FEERATESPERKWESTIMATES']._serialized_end=40614 + _globals['_FEERATESONCHAIN_FEE_ESTIMATES']._serialized_start=40617 + _globals['_FEERATESONCHAIN_FEE_ESTIMATES']._serialized_end=40900 + _globals['_FETCHINVOICEREQUEST']._serialized_start=40903 + _globals['_FETCHINVOICEREQUEST']._serialized_end=41264 + _globals['_FETCHINVOICERESPONSE']._serialized_start=41267 + _globals['_FETCHINVOICERESPONSE']._serialized_end=41421 + _globals['_FETCHINVOICECHANGES']._serialized_start=41424 + _globals['_FETCHINVOICECHANGES']._serialized_end=41682 + _globals['_FETCHINVOICENEXT_PERIOD']._serialized_start=41684 + _globals['_FETCHINVOICENEXT_PERIOD']._serialized_end=41810 + _globals['_FUNDCHANNELREQUEST']._serialized_start=41813 + _globals['_FUNDCHANNELREQUEST']._serialized_end=42320 + _globals['_FUNDCHANNELRESPONSE']._serialized_start=42323 + _globals['_FUNDCHANNELRESPONSE']._serialized_end=42552 + _globals['_FUNDCHANNELCHANNEL_TYPE']._serialized_start=42554 + _globals['_FUNDCHANNELCHANNEL_TYPE']._serialized_end=42630 + _globals['_GETROUTEREQUEST']._serialized_start=42633 + _globals['_GETROUTEREQUEST']._serialized_end=42869 + _globals['_GETROUTERESPONSE']._serialized_start=42871 + _globals['_GETROUTERESPONSE']._serialized_end=42924 + _globals['_GETROUTEROUTE']._serialized_start=42927 + _globals['_GETROUTEROUTE']._serialized_end=43124 + _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_start=43095 + _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_end=43124 + _globals['_LISTFORWARDSREQUEST']._serialized_start=43127 + _globals['_LISTFORWARDSREQUEST']._serialized_end=43566 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_start=43371 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_end=43447 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_start=43449 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_end=43494 + _globals['_LISTFORWARDSRESPONSE']._serialized_start=43568 + _globals['_LISTFORWARDSRESPONSE']._serialized_end=43635 + _globals['_LISTFORWARDSFORWARDS']._serialized_start=43638 + _globals['_LISTFORWARDSFORWARDS']._serialized_end=44336 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_start=44083 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_end=44167 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_start=44169 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_end=44217 + _globals['_LISTPAYSREQUEST']._serialized_start=44339 + _globals['_LISTPAYSREQUEST']._serialized_end=44558 + _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_start=44464 + _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_end=44519 + _globals['_LISTPAYSRESPONSE']._serialized_start=44560 + _globals['_LISTPAYSRESPONSE']._serialized_end=44611 + _globals['_LISTPAYSPAYS']._serialized_start=44614 + _globals['_LISTPAYSPAYS']._serialized_end=45253 + _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_start=45028 + _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_end=45087 + _globals['_LISTHTLCSREQUEST']._serialized_start=45255 + _globals['_LISTHTLCSREQUEST']._serialized_end=45297 + _globals['_LISTHTLCSRESPONSE']._serialized_start=45299 + _globals['_LISTHTLCSRESPONSE']._serialized_end=45354 + _globals['_LISTHTLCSHTLCS']._serialized_start=45357 + _globals['_LISTHTLCSHTLCS']._serialized_end=45622 + _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_start=45580 + _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_end=45622 + _globals['_PINGREQUEST']._serialized_start=45624 + _globals['_PINGREQUEST']._serialized_end=45713 + _globals['_PINGRESPONSE']._serialized_start=45715 + _globals['_PINGRESPONSE']._serialized_end=45745 + _globals['_SENDCUSTOMMSGREQUEST']._serialized_start=45747 + _globals['_SENDCUSTOMMSGREQUEST']._serialized_end=45799 + _globals['_SENDCUSTOMMSGRESPONSE']._serialized_start=45801 + _globals['_SENDCUSTOMMSGRESPONSE']._serialized_end=45840 + _globals['_SETCHANNELREQUEST']._serialized_start=45843 + _globals['_SETCHANNELREQUEST']._serialized_end=46141 + _globals['_SETCHANNELRESPONSE']._serialized_start=46143 + _globals['_SETCHANNELRESPONSE']._serialized_end=46206 + _globals['_SETCHANNELCHANNELS']._serialized_start=46209 + _globals['_SETCHANNELCHANNELS']._serialized_end=46667 + _globals['_SIGNINVOICEREQUEST']._serialized_start=46669 + _globals['_SIGNINVOICEREQUEST']._serialized_end=46708 + _globals['_SIGNINVOICERESPONSE']._serialized_start=46710 + _globals['_SIGNINVOICERESPONSE']._serialized_end=46747 + _globals['_SIGNMESSAGEREQUEST']._serialized_start=46749 + _globals['_SIGNMESSAGEREQUEST']._serialized_end=46786 + _globals['_SIGNMESSAGERESPONSE']._serialized_start=46788 + _globals['_SIGNMESSAGERESPONSE']._serialized_end=46858 + _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_start=46860 + _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_end=46939 + _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_start=46941 + _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_end=46987 + _globals['_WAITREQUEST']._serialized_start=46990 + _globals['_WAITREQUEST']._serialized_end=47239 + _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_start=47126 + _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_end=47183 + _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_start=47185 + _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_end=47239 + _globals['_WAITRESPONSE']._serialized_start=47242 + _globals['_WAITRESPONSE']._serialized_end=47469 + _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_start=47126 + _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_end=47183 + _globals['_STOPREQUEST']._serialized_start=47471 + _globals['_STOPREQUEST']._serialized_end=47484 + _globals['_STOPRESPONSE']._serialized_start=47486 + _globals['_STOPRESPONSE']._serialized_end=47500 + _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_start=47503 + _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_end=47670 + _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_start=47672 + _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_end=47699 + _globals['_PREAPPROVEINVOICEREQUEST']._serialized_start=47701 + _globals['_PREAPPROVEINVOICEREQUEST']._serialized_end=47759 + _globals['_PREAPPROVEINVOICERESPONSE']._serialized_start=47761 + _globals['_PREAPPROVEINVOICERESPONSE']._serialized_end=47788 + _globals['_STATICBACKUPREQUEST']._serialized_start=47790 + _globals['_STATICBACKUPREQUEST']._serialized_end=47811 + _globals['_STATICBACKUPRESPONSE']._serialized_start=47813 + _globals['_STATICBACKUPRESPONSE']._serialized_end=47848 + _globals['_NODE']._serialized_start=47851 + _globals['_NODE']._serialized_end=51853 # @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index 085c55467b8e..2be511d0070b 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -918,6 +918,7 @@ def listpeerchannels_channels2py(m): "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 + "last_stable_connection": m.last_stable_connection, # PrimitiveField in generate_composite "htlcs": [listpeerchannels_channels_htlcs2py(i) for i in m.htlcs], # ArrayField[composite] in generate_composite "close_to_addr": m.close_to_addr, # PrimitiveField in generate_composite }) @@ -967,6 +968,7 @@ def listclosedchannels_closedchannels2py(m): "last_commitment_txid": hexlify(m.last_commitment_txid), # PrimitiveField in generate_composite "last_commitment_fee_msat": amount2msat(m.last_commitment_fee_msat), # PrimitiveField in generate_composite "close_cause": str(m.close_cause), # EnumField in generate_composite + "last_stable_connection": m.last_stable_connection, # PrimitiveField in generate_composite }) diff --git a/doc/lightning-listclosedchannels.7.md b/doc/lightning-listclosedchannels.7.md index 182790cfc0af..31503aee2a1e 100644 --- a/doc/lightning-listclosedchannels.7.md +++ b/doc/lightning-listclosedchannels.7.md @@ -53,6 +53,7 @@ On success, an object containing **closedchannels** is returned. It is an array - **funding\_pushed\_msat** (msat, optional): How much `opener` pushed immediate (if non-zero) - **last\_commitment\_txid** (hash, optional): The final commitment tx's txid (or mutual close, if we accepted it). Not present for some very old, small channels pre-0.7.0. - **last\_commitment\_fee\_msat** (msat, optional): The fee on `last_commitment_txid` +- **last\_stable\_connection** (u64, optional): Last time we reestablished the open channel and stayed connected for 1 minute *(added v24.02)* [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -76,4 +77,4 @@ RESOURCES Main web site: Lightning -[comment]: # ( SHA256STAMP:1437321ab48ffcf8c5aa437db53eef72f5ed62dd1da7900483c86482e533a461) +[comment]: # ( SHA256STAMP:559d917217fe6d765d8bf019e46bf03d37dae9a437e530b1456252bcb901cbc9) diff --git a/doc/lightning-listpeerchannels.7.md b/doc/lightning-listpeerchannels.7.md index c7a2094336ea..8efacfc329ae 100644 --- a/doc/lightning-listpeerchannels.7.md +++ b/doc/lightning-listpeerchannels.7.md @@ -116,6 +116,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **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 +- **last\_stable\_connection** (u64, optional): Last time we reestablished the open channel and stayed connected for 1 minute *(added v24.02)* - **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 @@ -219,4 +220,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:8cad14f36d38722182a8fdccc393e93d121d97999d2487decfa09d53c991a8ae) +[comment]: # ( SHA256STAMP:c2ab91a2357b83264415f923b9d5195e5294a17b7424d9e75ac9ea704bea8fc5) diff --git a/doc/lightning-sql.7.md b/doc/lightning-sql.7.md index 478635366f1d..6c3afd714696 100644 --- a/doc/lightning-sql.7.md +++ b/doc/lightning-sql.7.md @@ -169,6 +169,7 @@ The following tables are currently supported: - `last_commitment_txid` (type `hash`, sqltype `BLOB`) - `last_commitment_fee_msat` (type `msat`, sqltype `INTEGER`) - `close_cause` (type `string`, sqltype `TEXT`) + - `last_stable_connection` (type `u64`, sqltype `INTEGER`) - `forwards` indexed by `in_channel and in_htlc_id` (see lightning-listforwards(7)) - `created_index` (type `u64`, sqltype `INTEGER`) @@ -341,6 +342,7 @@ The following tables are currently supported: - `out_offered_msat` (type `msat`, sqltype `INTEGER`) - `out_payments_fulfilled` (type `u64`, sqltype `INTEGER`) - `out_fulfilled_msat` (type `msat`, sqltype `INTEGER`) + - `last_stable_connection` (type `u64`, sqltype `INTEGER`) - related table `peerchannels_htlcs` - `row` (reference to `peerchannels.rowid`, sqltype `INTEGER`) - `arrindex` (index within array, sqltype `INTEGER`) @@ -531,4 +533,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:f17b33523de930b61d6397a730c40b2254929cf38930629b9be66f3d52947d0e) +[comment]: # ( SHA256STAMP:299446e88b1f847a82ae63c2087ce28a36b7c8fea2b102d4e3d87c420a9268e9) diff --git a/doc/schemas/listclosedchannels.schema.json b/doc/schemas/listclosedchannels.schema.json index b090bf2cbcc0..25bb800893c5 100644 --- a/doc/schemas/listclosedchannels.schema.json +++ b/doc/schemas/listclosedchannels.schema.json @@ -180,6 +180,11 @@ "onchain" ], "description": "What caused the channel to close" + }, + "last_stable_connection": { + "type": "u64", + "added": "v24.02", + "description": "Last time we reestablished the open channel and stayed connected for 1 minute" } } } diff --git a/doc/schemas/listpeerchannels.schema.json b/doc/schemas/listpeerchannels.schema.json index 5588b687ce9c..9e27aea18886 100644 --- a/doc/schemas/listpeerchannels.schema.json +++ b/doc/schemas/listpeerchannels.schema.json @@ -556,6 +556,11 @@ "type": "msat", "description": "Total amount of successful outgoing payment attempts" }, + "last_stable_connection": { + "type": "u64", + "added": "v24.02", + "description": "Last time we reestablished the open channel and stayed connected for 1 minute" + }, "htlcs": { "type": "array", "description": "current HTLCs in this channel", @@ -802,6 +807,7 @@ "out_payments_fulfilled": {}, "out_fulfilled_msat": {}, "out_msatoshi_fulfilled": {}, + "last_stable_connection": {}, "htlcs": {}, "initial_feerate": {}, "last_feerate": {}, @@ -895,6 +901,7 @@ "out_payments_fulfilled": {}, "out_fulfilled_msat": {}, "out_msatoshi_fulfilled": {}, + "last_stable_connection": {}, "htlcs": {}, "initial_feerate": {}, "last_feerate": {}, @@ -987,6 +994,7 @@ "out_payments_fulfilled": {}, "out_fulfilled_msat": {}, "out_msatoshi_fulfilled": {}, + "last_stable_connection": {}, "htlcs": {}, "initial_feerate": {}, "last_feerate": {}, @@ -1080,6 +1088,7 @@ "out_payments_fulfilled": {}, "out_fulfilled_msat": {}, "out_msatoshi_fulfilled": {}, + "last_stable_connection": {}, "htlcs": {}, "inflight": {}, "close_to_addr": {}, diff --git a/lightningd/closed_channel.c b/lightningd/closed_channel.c index 67f24cad983a..11ae1133df59 100644 --- a/lightningd/closed_channel.c +++ b/lightningd/closed_channel.c @@ -76,6 +76,10 @@ static void json_add_closed_channel(struct json_stream *response, } json_add_string(response, "close_cause", channel_change_state_reason_str(channel->state_change_cause)); + if (channel->last_stable_connection != 0) { + json_add_u64(response, "last_stable_connection", + channel->last_stable_connection); + } json_object_end(response); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 9d6c1605b795..0feeade9e9cd 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -865,6 +865,11 @@ static void json_add_channel(struct lightningd *ld, json_object_end(response); } json_object_end(response); + + if (channel->last_stable_connection != 0) { + json_add_u64(response, "last_stable_connection", + channel->last_stable_connection); + } } json_add_string(response, "state", channel_state_name(channel)); diff --git a/tests/test_connection.py b/tests/test_connection.py index 02fa5c282189..726ec17998f2 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4456,3 +4456,34 @@ def test_offline_fd_check(node_factory): # if get_node starts it, it'll expect an address, so do it manually. l1 = node_factory.get_node(options={"offline": None}, start=False) l1.daemon.start() + + +def test_last_stable_connection(node_factory): + l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True}) + + # We wait a minute to be stable. + STABLE_TIME = 60 + assert 'last_stable_connection' not in only_one(l1.rpc.listpeerchannels()['channels']) + assert 'last_stable_connection' not in only_one(l2.rpc.listpeerchannels()['channels']) + + recon_time = time.time() + + # This take a minute, so don't fail if TIMEOUT is set to 10. + wait_for(lambda: 'last_stable_connection' in only_one(l1.rpc.listpeerchannels()['channels']), timeout=STABLE_TIME + 15) + l1stable = only_one(l1.rpc.listpeerchannels()['channels'])['last_stable_connection'] + wait_for(lambda: 'last_stable_connection' in only_one(l2.rpc.listpeerchannels()['channels'])) + l2stable = only_one(l2.rpc.listpeerchannels()['channels'])['last_stable_connection'] + + # Disconnect, and/or restart then reconnect. + l1.rpc.disconnect(l2.info['id'], force=True) + recon_time = time.time() + l2.restart() + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + assert only_one(l1.rpc.listpeerchannels()['channels'])['last_stable_connection'] == l1stable + assert only_one(l2.rpc.listpeerchannels()['channels'])['last_stable_connection'] == l2stable + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['last_stable_connection'] != l1stable, timeout=STABLE_TIME + 15) + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['last_stable_connection'] != l2stable) + + assert only_one(l1.rpc.listpeerchannels()['channels'])['last_stable_connection'] > recon_time + STABLE_TIME + assert only_one(l2.rpc.listpeerchannels()['channels'])['last_stable_connection'] > recon_time + STABLE_TIME diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ccc8667b05e3..f9c77fe70bf6 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -3506,7 +3506,9 @@ def test_sql(node_factory, bitcoind): {'name': 'last_commitment_fee_msat', 'type': 'msat'}, {'name': 'close_cause', - 'type': 'string'}]}, + 'type': 'string'}, + {'name': 'last_stable_connection', + 'type': 'u64'}]}, 'closedchannels_channel_type_bits': { 'columns': [{'name': 'row', 'type': 'u64'}, @@ -3840,6 +3842,8 @@ def test_sql(node_factory, bitcoind): 'type': 'u64'}, {'name': 'out_fulfilled_msat', 'type': 'msat'}, + {'name': 'last_stable_connection', + 'type': 'u64'}, {'name': 'reestablished', 'type': 'boolean'}, {'name': 'close_to_addr', From 41b7da5183393be95016f73a73a5430e0342af60 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:20 +1030 Subject: [PATCH 34/38] gossipd: make seeker uintmap visible to memleak code. ``` gossipd: MEMLEAK: 0x55e22ac0f8e8 gossipd: label=gossipd/seeker.c:621:u8 gossipd: alloc: gossipd: ccan/ccan/tal/tal.c:477 (tal_alloc_) gossipd: gossipd/seeker.c:621 (check_timestamps) gossipd: gossipd/seeker.c:645 (process_scid_probe) gossipd: gossipd/queries.c:849 (handle_reply_channel_range) gossipd: gossipd/gossipd.c:436 (handle_recv_gossip) gossipd: gossipd/gossipd.c:509 (connectd_req) gossipd: common/daemon_conn.c:35 (handle_read) gossipd: ccan/ccan/io/io.c:59 (next_plan) gossipd: ccan/ccan/io/io.c:407 (do_plan) gossipd: ccan/ccan/io/io.c:417 (io_ready) gossipd: ccan/ccan/io/poll.c:453 (io_loop) gossipd: gossipd/gossipd.c:950 (main) gossipd: parents: gossipd: gossipd/seeker.c:132:struct seeker ``` Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 1 + gossipd/seeker.c | 6 ++++++ gossipd/seeker.h | 2 ++ gossipd/test/run-next_block_range.c | 3 +++ 4 files changed, 12 insertions(+) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index b20118c98e46..af6ec048c1ec 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -716,6 +716,7 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) /* Now delete daemon and those which it has pointers to. */ memleak_scan_obj(memtable, daemon); memleak_scan_htable(memtable, &daemon->peers->raw); + dev_seeker_memleak(memtable, daemon->seeker); found_leak = dump_memleak(memtable, memleak_status_broken, NULL); daemon_conn_send(daemon->master, diff --git a/gossipd/seeker.c b/gossipd/seeker.c index 77500b34afa9..159b0a2c1cb9 100644 --- a/gossipd/seeker.c +++ b/gossipd/seeker.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -975,3 +976,8 @@ void seeker_peer_gone(struct seeker *seeker, const struct peer *peer) if (seeker->preferred_peer == peer) seeker->preferred_peer = NULL; } + +void dev_seeker_memleak(struct htable *memtable, struct seeker *seeker) +{ + memleak_scan_uintmap(memtable, &seeker->stale_scids); +} diff --git a/gossipd/seeker.h b/gossipd/seeker.h index dce637935fce..2edd2662355d 100644 --- a/gossipd/seeker.h +++ b/gossipd/seeker.h @@ -25,4 +25,6 @@ bool add_unknown_scid(struct seeker *seeker, void seeker_peer_gone(struct seeker *seeker, const struct peer *peer); + +void dev_seeker_memleak(struct htable *memtable, struct seeker *seeker); #endif /* LIGHTNING_GOSSIPD_SEEKER_H */ diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index dcee25621b9f..dfebada2e481 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -30,6 +30,9 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, struct peer *first_random_peer(struct daemon *daemon UNNEEDED, struct peer_node_id_map_iter *it UNNEEDED) { fprintf(stderr, "first_random_peer 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 new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, From f9ff45659b4c503ebfb346562de2c8020236f856 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:20 +1030 Subject: [PATCH 35/38] lightingd: corrections from Alex Myers's review. Signed-off-by: Rusty Russell --- lightningd/channel_gossip.c | 2 +- lightningd/channel_gossip.h | 2 +- lightningd/gossip_generation.c | 6 +++++- tests/test_connection.py | 10 +++++----- tests/test_gossip.py | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 5905d49b7593..e048d2f62990 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -925,7 +925,7 @@ const u8 *channel_gossip_update_for_error(const tal_t *ctx, * - MUST set the `short_channel_id` to either an `alias` it has * received from the peer, or the real channel `short_channel_id`. */ -/* But we used to get this wrong! Sot this is the only place where we +/* But we used to get this wrong! So this is the only place where we * look up by *remote* id. It's not unique, but it is unique for a * specific peer. */ static struct channel *lookup_by_peer_remote_alias(struct lightningd *ld, diff --git a/lightningd/channel_gossip.h b/lightningd/channel_gossip.h index 9170a98ade55..0a81089dea14 100644 --- a/lightningd/channel_gossip.h +++ b/lightningd/channel_gossip.h @@ -49,7 +49,7 @@ void channel_gossip_channel_reestablished(struct channel *channel); /* Peer has disconnected */ void channel_gossip_channel_disconnect(struct channel *channel); -/* Gossipd sent us this channel_update about the peer's side of the channle */ +/* Gossipd sent us this channel_update about the peer's side of the channel */ void channel_gossip_set_remote_update(struct lightningd *ld, const struct peer_update *update TAKES, const struct node_id *source); diff --git a/lightningd/gossip_generation.c b/lightningd/gossip_generation.c index 1d20bccc81f0..afe09a54f79e 100644 --- a/lightningd/gossip_generation.c +++ b/lightningd/gossip_generation.c @@ -125,6 +125,10 @@ u8 *unsigned_channel_update(const tal_t *ctx, /* Make sure timestamp changes! */ timestamp = time_now().ts.tv_sec; + /* FIXME: @endothermicdev points out that our clock could be + * wrong once, and now we'll keep producing future timestamps. + * We could sanity check that old_timestamp is within 2 weeks and + * discard? */ if (old_timestamp && timestamp <= *old_timestamp) timestamp = *old_timestamp + 1; @@ -347,7 +351,7 @@ static const struct wireaddr *gather_addresses(const tal_t *ctx, { struct wireaddr *addrs; - /* Note: If no announceable, tal_dup_talarr returns NULL! */ + /* Note: If ld->announceable is NULL, tal_dup_talarr returns NULL! */ addrs = tal_dup_talarr(ctx, struct wireaddr, ld->announceable); if (!addrs) addrs = tal_arr(ctx, struct wireaddr, 0); diff --git a/tests/test_connection.py b/tests/test_connection.py index 726ec17998f2..c69b35bee7c9 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -111,7 +111,7 @@ def test_remote_addr(node_factory, bitcoind): def_port = default_ln_port(l2.info["network"]) - # when we restart l1 with a channel and reconnect, node_annoucement update + # when we restart l1 with a channel and reconnect, node_announcement 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 @@ -125,7 +125,7 @@ def test_remote_addr(node_factory, bitcoind): 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 trigger `node_annoucement` update. + # connect second node. This will trigger `node_announcement` update. 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:{}".format(def_port)) @@ -146,7 +146,7 @@ def test_remote_addr(node_factory, bitcoind): def test_remote_addr_disabled(node_factory, bitcoind): - """Simply tests that IP address discovery annoucements can be turned off + """Simply tests that IP address discovery announcements can be turned off We perform logic tests on L2, setup: l1 --> [l2] <-- l3 @@ -179,7 +179,7 @@ def test_remote_addr_disabled(node_factory, bitcoind): l2.daemon.wait_for_log(f"{l3.info['id']}.*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... + # node_announcement by now. Check we didn't... bitcoind.generate_block(6) # ugly, but we need to wait for gossip... assert not l2.daemon.is_in_log("Update our node_announcement for discovered address") @@ -225,7 +225,7 @@ def test_remote_addr_port(node_factory, bitcoind): l2.rpc.connect(l3.info['id'], 'localhost', l3.port) # if ip discovery would have been enabled, we would have send an updated - # node_annoucement by now. Check we didn't... + # node_announcement by now. Check we didn't... l2.daemon.wait_for_logs(["Already have funding locked in", "Update our node_announcement for discovered address"]) info = l2.rpc.getinfo() diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 3851a3939429..beaba22671fb 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -178,7 +178,7 @@ def test_announce_dns_suppressed(node_factory, bitcoind): def test_announce_and_connect_via_dns(node_factory, bitcoind): - """ Test that DNS annoucements propagate and can be used when connecting. + """ Test that DNS announcements propagate and can be used when connecting. - First node announces only a FQDN like 'localhost.localdomain'. - Second node gets a channel with first node. From a0d18ba8932fb14d0a148580cb0a9cf10ca9defc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:20 +1030 Subject: [PATCH 36/38] pytest: fix flake in test_wumbo_channels We mine blocks too fast, and l3 discard the channel_announcment as too far in the future: ``` lightningd-3 2023-12-14T06:40:04.744Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-gossipd: Ignoring future channel_announcment for 103x1x1 (current block 102) ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 7 ++++++- tests/test_gossip.py | 2 ++ tests/test_splicing.py | 7 +++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index c69b35bee7c9..7b4f435f954d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1891,7 +1891,10 @@ def test_multifunding_simple(node_factory, bitcoind): "amount": 50000}] l1.rpc.multifundchannel(destinations) - bitcoind.generate_block(6, wait_for_mempool=1) + bitcoind.generate_block(1, wait_for_mempool=1) + # Don't have others reject channel_announcement as too far in future. + sync_blockheight(bitcoind, [l1, l2, l3, l4]) + bitcoind.generate_block(5) for node in [l1, l2, l3, l4]: node.daemon.wait_for_log(r'to CHANNELD_NORMAL') @@ -3471,6 +3474,8 @@ def test_wumbo_channels(node_factory, bitcoind): # Get that mined, and announced. bitcoind.generate_block(6, wait_for_mempool=1) + # Make sure l3 is ready to receive channel announcement! + sync_blockheight(bitcoind, [l1, l2, l3]) # Connect l3, get gossip. l3.rpc.connect(l1.info['id'], 'localhost', port=l1.port) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index beaba22671fb..552c118f5bd4 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1090,6 +1090,8 @@ def test_gossip_lease_rates(node_factory, bitcoind): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, 10**6) + # Don't have l2 reject channel_announcement as too far in future. + sync_blockheight(bitcoind, [l1, l2]) # Announce depth is ALWAYS 6 blocks bitcoind.generate_block(5) diff --git a/tests/test_splicing.py b/tests/test_splicing.py index 87a3cf4994a8..019eaaf29b72 100644 --- a/tests/test_splicing.py +++ b/tests/test_splicing.py @@ -5,7 +5,7 @@ import unittest import time from utils import ( - wait_for, TEST_NETWORK, first_scid, only_one + sync_blockheight, wait_for, TEST_NETWORK, first_scid, only_one ) @@ -313,7 +313,10 @@ def test_splice_stuck_htlc(node_factory, bitcoind, executor): assert len(list(mempool.keys())) == 1 assert result['txid'] in list(mempool.keys()) - bitcoind.generate_block(6, wait_for_mempool=1) + bitcoind.generate_block(1, wait_for_mempool=1) + # Don't have l2, l3 reject channel_announcement as too far in future. + sync_blockheight(bitcoind, [l1, l2, l3]) + bitcoind.generate_block(5) l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') From fffb765d24d50949a51f267131e60a90d8f57763 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:20 +1030 Subject: [PATCH 37/38] pytest: don't trigger bad gossip message in test_routing_gossip_reconnect. ``` 2024-01-29T21:26:50.9785559Z lightningd-1 2024-01-29T21:14:09.709Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-gossipd: Ignoring future channel_announcment for 110x1x0 (current block 109) 2024-01-29T21:26:50.9786945Z lightningd-1 2024-01-29T21:14:09.710Z UNUSUAL lightningd: Bad gossip order: could not find channel 110x1x0 for peer's channel update ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 4 ++-- tests/test_gossip.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 7b4f435f954d..8eadd6e85cf9 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1857,7 +1857,7 @@ def test_multifunding_v2_exclusive(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], num_blocks=6, wait_for_mempool=1) for node in [l1, l2, l3, l4]: node.daemon.wait_for_log(r'to CHANNELD_NORMAL') @@ -4023,7 +4023,7 @@ def test_multichan_stress(node_factory, executor, bitcoind): assert(len(l3.rpc.listpeerchannels(l2.info['id'])['channels']) == 2) # Make sure gossip works. - bitcoind.generate_block(6, wait_for_mempool=1) + mine_funding_to_announce(bitcoind, [l1, l2, l3], num_blocks=6, wait_for_mempool=1) wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 2) def send_many_payments(): diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 552c118f5bd4..9520fbf164e9 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -599,7 +599,7 @@ def non_public(node): wait_for(lambda: non_public(l4) == [scid34]) -def test_routing_gossip_reconnect(node_factory): +def test_routing_gossip_reconnect(node_factory, bitcoind): # Connect two peers, reconnect and then see if we resume the # gossip. disconnects = ['-WIRE_CHANNEL_ANNOUNCEMENT'] @@ -608,9 +608,15 @@ def test_routing_gossip_reconnect(node_factory): 'may_reconnect': True}, {'may_reconnect': True}, {}]) + # Make sure everyone is up to block height so we don't get bad gossip msgs! + sync_blockheight(bitcoind, [l1, l2, l3]) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.openchannel(l2, CHANNEL_SIZE) + # Make sure everyone is up to block height so we don't get bad gossip msgs! + sync_blockheight(bitcoind, [l1, l2, l3]) + # Now open new channels and everybody should sync l2.rpc.connect(l3.info['id'], 'localhost', l3.port) l2.openchannel(l3, CHANNEL_SIZE) From c5e43078893e60c28d699eb7df8acd1b84c66819 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Jan 2024 13:46:20 +1030 Subject: [PATCH 38/38] lightningd: don't generate node_announcements with identical timestamps. This caused a flake in test_gossip_lease_rates, since the peer would ignore the node_announcement due to duplicate timestamps! Signed-off-by: Rusty Russell --- lightningd/channel_gossip.c | 2 +- lightningd/gossip_generation.c | 45 ++++++++++++++++++++++++++++++++-- lightningd/gossip_generation.h | 5 +++- tests/test_gossip.py | 4 --- wallet/test/run-wallet.c | 4 ++- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index e048d2f62990..97a9d81d73e8 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -1065,7 +1065,7 @@ void channel_gossip_node_announce(struct lightningd *ld) if (!gossipd_init_done) return; - nannounce = unsigned_node_announcement(tmpctx, ld); + nannounce = unsigned_node_announcement(tmpctx, ld, ld->node_announcement); /* Don't bother with duplicates */ if (ld->node_announcement diff --git a/lightningd/gossip_generation.c b/lightningd/gossip_generation.c index afe09a54f79e..0cbb78468c3a 100644 --- a/lightningd/gossip_generation.c +++ b/lightningd/gossip_generation.c @@ -304,6 +304,39 @@ static void get_nannounce_parts(const u8 *node_announcement, parts[2] = NULL; } +/* Get timestamp of a (valid!) node_announcement */ +static u32 get_nannounce_timestamp(const u8 *node_announcement) +{ + const u8 *p; + u16 flen; + size_t len; + u32 timestamp; + + /* BOLT #7: + * + * 1. type: 257 (`node_announcement`) + * 2. data: + * * [`signature`:`signature`] + * * [`u16`:`flen`] + * * [`flen*byte`:`features`] + * * [`u32`:`timestamp`] + *... + */ + len = tal_count(node_announcement); + p = node_announcement; + + /* Note: 2 bytes for `type` field */ + fromwire_u16(&p, &len); + fromwire(&p, &len, NULL, 64); + flen = fromwire_u16(&p, &len); + fromwire(&p, &len, NULL, flen); + + timestamp = fromwire_u32(&p, &len); + assert(p != NULL); + + return timestamp; +} + /* Is nann1 same as nann2 (not sigs and timestamps)? */ bool node_announcement_same(const u8 *nann1, const u8 *nann2) { @@ -379,16 +412,24 @@ static const struct wireaddr *gather_addresses(const tal_t *ctx, } u8 *unsigned_node_announcement(const tal_t *ctx, - struct lightningd *ld) + struct lightningd *ld, + const u8 *prev) { secp256k1_ecdsa_signature sig; const struct wireaddr *addrs; + u32 timestamp = time_now().ts.tv_sec; addrs = gather_addresses(tmpctx, ld); + /* Even if we're quick, don't duplicate timestamps! */ + if (prev) { + u32 old_timestamp = get_nannounce_timestamp(prev); + if (timestamp <= old_timestamp) + timestamp = old_timestamp + 1; + } memset(&sig, 0, sizeof(sig)); return create_nannounce(tmpctx, ld, &sig, - addrs, time_now().ts.tv_sec, + addrs, timestamp, ld->lease_rates); } diff --git a/lightningd/gossip_generation.h b/lightningd/gossip_generation.h index 20769a11f9e4..85d08592481b 100644 --- a/lightningd/gossip_generation.h +++ b/lightningd/gossip_generation.h @@ -81,8 +81,11 @@ const char *check_announce_sigs(const struct channel *channel, * unsigned_node_announcement: create a current unsigned node announcement. * @ctx: the context to allocate return from * @ld: the lightningd struct. + * @prev: optional previous announcement (to ensure we increase timestamp!) */ -u8 *unsigned_node_announcement(const tal_t *ctx, struct lightningd *ld); +u8 *unsigned_node_announcement(const tal_t *ctx, + struct lightningd *ld, + const u8 *prev); /** * add_node_announcement_sig: apply the signature to the node announcement diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 9520fbf164e9..bcf5c488e41e 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1073,10 +1073,6 @@ def test_gossip_lease_rates(node_factory, bitcoind): 'channel-fee-max-proportional-thousandths': 200} l1, l2 = node_factory.get_nodes(2, opts=[lease_opts, {}]) - # These logs happen during startup, start looking from the beginning - l1.daemon.logsearch_start = 0 - l2.daemon.logsearch_start = 0 - rates = l1.rpc.call('funderupdate') assert rates['channel_fee_max_base_msat'] == Millisatoshi('500000msat') assert rates['channel_fee_max_proportional_thousandths'] == 200 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 3c3320acf7e0..04160f03b1c9 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1171,7 +1171,9 @@ u8 *unsigned_channel_update(const tal_t *ctx UNNEEDED, bool enabled UNNEEDED) { fprintf(stderr, "unsigned_channel_update called!\n"); abort(); } /* Generated stub for unsigned_node_announcement */ -u8 *unsigned_node_announcement(const tal_t *ctx UNNEEDED, struct lightningd *ld UNNEEDED) +u8 *unsigned_node_announcement(const tal_t *ctx UNNEEDED, + struct lightningd *ld UNNEEDED, + const u8 *prev UNNEEDED) { fprintf(stderr, "unsigned_node_announcement called!\n"); abort(); } /* Generated stub for unwrap_onionreply */ u8 *unwrap_onionreply(const tal_t *ctx UNNEEDED,