diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d2b010a4b3ed..47ccd1906937 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -263,6 +263,15 @@ jobs: TEST_NETWORK: regtest DEVELOPER: 1 EXPERIMENTAL_DUAL_FUND: 1 + # And splicing! + - NAME: splicing + CFG: gcc-dev1 + TEST_DB_PROVIDER: sqlite3 + COMPILER: gcc + TEST_NETWORK: regtest + DEVELOPER: 1 + EXPERIMENTAL_DUAL_FUND: 1 + EXPERIMENTAL_SPLICING: 1 steps: - name: Checkout uses: actions/checkout@v3 @@ -290,6 +299,7 @@ jobs: DEVELOPER: ${{ matrix.DEVELOPER }} COMPILER: ${{ matrix.COMPILER }} EXPERIMENTAL_DUAL_FUND: ${{ matrix.EXPERIMENTAL_DUAL_FUND }} + EXPERIMENTAL_SPLICING: ${{ matrix.EXPERIMENTAL_SPLICING }} COMPAT: 1 CFG: ${{ matrix.CFG }} SLOW_MACHINE: 1 @@ -391,7 +401,7 @@ jobs: - NAME: ASan/UBSan (01/10) PYTEST_OPTS: --test-group=1 --test-group-count=10 - NAME: ASan/UBSan (02/10) - PYTEST_OPTS: --test-group=2 --test-group-count=10 + PYTEST_OPTS: --test-group=2 --test-group-count=10 -n 1 - NAME: ASan/UBSan (03/10) PYTEST_OPTS: --test-group=3 --test-group-count=10 - NAME: ASan/UBSan (04/10) diff --git a/.msggen.json b/.msggen.json index 9b6ad7c2176c..e53b7cdf404a 100644 --- a/.msggen.json +++ b/.msggen.json @@ -182,6 +182,7 @@ "ListpeerchannelsChannelsState": { "AWAITING_UNILATERAL": 6, "CHANNELD_AWAITING_LOCKIN": 1, + "CHANNELD_AWAITING_SPLICE": 11, "CHANNELD_NORMAL": 2, "CHANNELD_SHUTTING_DOWN": 3, "CLOSINGD_COMPLETE": 5, @@ -1131,6 +1132,7 @@ "ListPeerChannels.channels[].inflight[].funding_txid": 1, "ListPeerChannels.channels[].inflight[].our_funding_msat": 5, "ListPeerChannels.channels[].inflight[].scratch_txid": 6, + "ListPeerChannels.channels[].inflight[].splice_amount": 7, "ListPeerChannels.channels[].inflight[].total_funding_msat": 4 }, "ListpeerchannelsChannelsState_changes": { @@ -1241,6 +1243,7 @@ "ListPeers.peers[].channels[].inflight[].funding_txid": 1, "ListPeers.peers[].channels[].inflight[].our_funding_msat": 5, "ListPeers.peers[].channels[].inflight[].scratch_txid": 6, + "ListPeers.peers[].channels[].inflight[].splice_amount": 7, "ListPeers.peers[].channels[].inflight[].total_funding_msat": 4 }, "ListpeersPeersLog": { @@ -4078,6 +4081,10 @@ "added": "v23.02", "deprecated": false }, + "ListPeerChannels.channels[].inflight[].splice_amount": { + "added": "v23.08", + "deprecated": false + }, "ListPeerChannels.channels[].inflight[].total_funding_msat": { "added": "v23.02", "deprecated": false @@ -4422,6 +4429,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListPeers.peers[].channels[].inflight[].splice_amount": { + "added": "v23.08", + "deprecated": false + }, "ListPeers.peers[].channels[].inflight[].total_funding_msat": { "added": "pre-v0.10.1", "deprecated": false diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index fe9510b25f4b..d526596118f8 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -95,7 +95,7 @@ bool psbt_is_finalized(const struct wally_psbt *psbt) } struct wally_psbt_input *psbt_add_input(struct wally_psbt *psbt, - struct wally_tx_input *input, + const struct wally_tx_input *input, size_t insert_at) { const u32 flags = WALLY_PSBT_FLAG_NON_FINAL; /* Skip script/witness */ @@ -322,6 +322,15 @@ void psbt_input_set_utxo(struct wally_psbt *psbt, size_t in, assert(wally_err == WALLY_OK); } +void psbt_input_set_outpoint(struct wally_psbt *psbt, size_t in, + struct bitcoin_outpoint outpoint) +{ + assert(in < psbt->num_inputs); + psbt->inputs[in].index = outpoint.n; + memcpy(psbt->inputs[in].txhash, &outpoint.txid, + sizeof(struct bitcoin_txid)); +} + void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript) { int wally_err; @@ -446,6 +455,28 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, return val; } +size_t psbt_input_get_weight(const struct wally_psbt *psbt, + size_t in) +{ + size_t weight; + const struct wally_map_item *redeem_script; + + redeem_script = wally_map_get_integer(&psbt->inputs[in].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04); + + /* txid + txout + sequence */ + weight = (32 + 4 + 4) * 4; + if (redeem_script) { + weight += + (redeem_script->value_len + + varint_size(redeem_script->value_len)) * 4; + } else { + /* zero scriptSig length */ + weight += varint_size(0) * 4; + } + + return weight; +} + struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt, size_t out) { @@ -456,6 +487,13 @@ struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt, return amount_asset_to_sat(&asset); } +size_t psbt_output_get_weight(const struct wally_psbt *psbt, + size_t outnum) +{ + return (8 /* amount*/ + varint_size(psbt->outputs[outnum].script_len) + + psbt->outputs[outnum].script_len) * 4; +} + static void add(u8 **key, const void *mem, size_t len) { size_t oldlen = tal_count(*key); @@ -733,6 +771,12 @@ const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt, return bytes; } +bool validate_psbt(const struct wally_psbt *psbt) +{ + size_t len; + return wally_psbt_get_length(psbt, 0, &len) == WALLY_OK; +} + struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes, size_t byte_len) { diff --git a/bitcoin/psbt.h b/bitcoin/psbt.h index 309dd9517d32..dc22b3bbe926 100644 --- a/bitcoin/psbt.h +++ b/bitcoin/psbt.h @@ -108,7 +108,7 @@ struct wally_tx *psbt_final_tx(const tal_t *ctx, const struct wally_psbt *psbt); u8 *psbt_make_key(const tal_t *ctx, u8 key_subtype, const u8 *key_data); struct wally_psbt_input *psbt_add_input(struct wally_psbt *psbt, - struct wally_tx_input *input, + const struct wally_tx_input *input, size_t insert_at); /* One stop shop for adding an input + metadata to a PSBT */ @@ -127,6 +127,9 @@ void psbt_input_set_wit_utxo(struct wally_psbt *psbt, size_t in, void psbt_input_set_utxo(struct wally_psbt *psbt, size_t in, const struct wally_tx *prev_tx); +void psbt_input_set_outpoint(struct wally_psbt *psbt, size_t in, + struct bitcoin_outpoint outpoint); + /* psbt_elements_input_set_asset - Set the asset/value fields for an * Elements PSBT (PSET, technically */ void psbt_elements_input_set_asset(struct wally_psbt *psbt, size_t in, @@ -211,6 +214,10 @@ void psbt_output_set_unknown(const tal_t *ctx, struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, size_t in); +/* psbt_input_get_weight - Calculate the tx weight for input index `in` */ +size_t psbt_input_get_weight(const struct wally_psbt *psbt, + size_t in); + /* psbt_output_get_amount - Returns the value of this output * * @psbt - psbt @@ -219,6 +226,10 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt, size_t out); +/* psbt_output_get_weight - Calculate the tx weight for output index `outnum` */ +size_t psbt_output_get_weight(const struct wally_psbt *psbt, + size_t outnum); + /* psbt_compute_fee - Returns value of fee for PSBT * * @psbt -psbt @@ -266,6 +277,7 @@ struct wally_psbt *psbt_from_b64(const tal_t *ctx, char *psbt_to_b64(const tal_t *ctx, const struct wally_psbt *psbt); const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt, size_t *bytes_written); +bool validate_psbt(const struct wally_psbt *psbt); struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes, size_t byte_len); void towire_wally_psbt(u8 **pptr, const struct wally_psbt *psbt); diff --git a/channeld/Makefile b/channeld/Makefile index 5e98db2f0147..894929b3ed01 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -13,6 +13,8 @@ CHANNELD_HEADERS := \ CHANNELD_SRC := channeld/channeld.c \ channeld/commit_tx.c \ channeld/full_channel.c \ + channeld/splice.c \ + channeld/inflight.c \ channeld/channeld_wiregen.c \ channeld/watchtower.c @@ -25,8 +27,13 @@ ALL_C_HEADERS += $(CHANNELD_HEADERS) ALL_PROGRAMS += lightningd/lightning_channeld # Here's what lightningd depends on -LIGHTNINGD_CONTROL_HEADERS += channeld/channeld_wiregen.h -LIGHTNINGD_CONTROL_OBJS += channeld/channeld_wiregen.o +LIGHTNINGD_CONTROL_HEADERS += \ + channeld/channeld_wiregen.h \ + channeld/inflight.h +LIGHTNINGD_CONTROL_OBJS += \ + channeld/channeld_wiregen.o \ + channeld/inflight.o + # Common source we use. CHANNELD_COMMON_OBJS := \ @@ -53,6 +60,7 @@ CHANNELD_COMMON_OBJS := \ common/status_wiregen.o \ common/gossip_store.o \ common/hmac.o \ + common/interactivetx.o \ common/htlc_state.o \ common/htlc_trim.o \ common/htlc_tx.o \ @@ -73,6 +81,7 @@ CHANNELD_COMMON_OBJS := \ common/ping.o \ 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 \ diff --git a/channeld/channeld.c b/channeld/channeld.c index 0d7a39c2cc95..98d728c78a51 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -11,6 +11,7 @@ * limits, unlikely as that is. */ #include "config.h" +#include #include #include #include @@ -18,10 +19,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -31,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -42,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +57,10 @@ #define MASTER_FD STDIN_FILENO #define HSM_FD 4 +#define VALID_STFU_MESSAGE(msg) \ + ((msg) == WIRE_SPLICE || \ + (msg) == WIRE_SPLICE_ACK) + struct peer { struct per_peer_state *pps; bool channel_ready[NUM_SIDES]; @@ -112,6 +123,7 @@ struct peer { 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; @@ -139,14 +151,23 @@ struct peer { /* If master told us to send wrong_funding */ struct bitcoin_outpoint *shutdown_wrong_funding; - /* Do we want quiescence? */ - bool stfu; + /* Do we want quiescence? + * Note: This flag is needed seperately from `stfu_sent` so we can + * detect the entering "stfu" mode. */ + bool want_stfu; /* Which side is considered the initiator? */ enum side stfu_initiator; /* Has stfu been sent by each side? */ bool stfu_sent[NUM_SIDES]; + /* After STFU mode is enabled, wait for a signle message flag */ + bool stfu_wait_single_msg; /* Updates master asked, which we've deferred while quiescing */ struct msg_queue *update_queue; + /* Callback for when when stfu is negotiated successfully */ + void (*on_stfu_success)(struct peer*); + + struct splice_state *splice_state; + struct splice *splice; #if DEVELOPER /* If set, don't fire commit counter when this hits 0 */ @@ -228,12 +249,32 @@ const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) return msg; } +static bool is_stfu_active(const struct peer *peer) +{ + return peer->stfu_sent[LOCAL] && peer->stfu_sent[REMOTE]; +} + +static void end_stfu_mode(struct peer *peer) +{ + peer->want_stfu = false; + peer->stfu_sent[LOCAL] = peer->stfu_sent[REMOTE] = false; + peer->stfu_wait_single_msg = false; + peer->on_stfu_success = NULL; + + status_debug("Left STFU mode."); +} + static void maybe_send_stfu(struct peer *peer) { - if (!peer->stfu) + if (!peer->want_stfu) return; - if (!peer->stfu_sent[LOCAL] && !pending_updates(peer->channel, LOCAL, false)) { + if (pending_updates(peer->channel, LOCAL, false)) { + status_info("Pending updates prevent us from STFU mode at this" + " time."); + } + else if (!peer->stfu_sent[LOCAL]) { + status_debug("Sending peer that we want to STFU."); u8 *msg = towire_stfu(NULL, &peer->channel_id, peer->stfu_initiator == LOCAL); peer_write(peer->pps, take(msg)); @@ -241,9 +282,21 @@ static void maybe_send_stfu(struct peer *peer) } if (peer->stfu_sent[LOCAL] && peer->stfu_sent[REMOTE]) { + /* Prevent STFU mode being inadvertantly activated twice during + * splice. This occurs because the commit -> revoke_and_ack + * cycle calls into `maybe_send_stfu`. The `want_stfu` flag is + * to prevent triggering the entering of stfu events twice. */ + peer->want_stfu = false; status_unusual("STFU complete: we are quiescent"); wire_sync_write(MASTER_FD, towire_channeld_dev_quiesce_reply(tmpctx)); + + peer->stfu_wait_single_msg = true; + status_unusual("STFU complete: setting stfu_wait_single_msg = true"); + if (peer->on_stfu_success) { + peer->on_stfu_success(peer); + peer->on_stfu_success = NULL; + } } } @@ -276,13 +329,15 @@ static void handle_stfu(struct peer *peer, const u8 *stfu) peer_failed_warn(peer->pps, &peer->channel_id, "STFU but you still have updates pending?"); - if (!peer->stfu) { - peer->stfu = true; + if (!peer->want_stfu) { + peer->want_stfu = true; if (!remote_initiated) peer_failed_warn(peer->pps, &peer->channel_id, "Unsolicited STFU but you said" " you didn't initiate?"); peer->stfu_initiator = REMOTE; + + status_debug("STFU initiator was remote."); } else { /* BOLT-quiescent #2: * @@ -291,8 +346,13 @@ static void handle_stfu(struct peer *peer, const u8 *stfu) * arbitrarily considered to be the channel funder (the sender * of `open_channel`). */ - if (remote_initiated) + if (remote_initiated) { + status_debug("Dual STFU intiation tiebreaker. Setting initiator to %s", + peer->channel->opener == LOCAL ? "LOCAL" : "REMOTE"); peer->stfu_initiator = peer->channel->opener; + } else { + status_debug("STFU initiator local."); + } } /* BOLT-quiescent #2: @@ -311,7 +371,7 @@ static void handle_stfu(struct peer *peer, const u8 *stfu) /* Returns true if we queued this for later handling (steals if true) */ static bool handle_master_request_later(struct peer *peer, const u8 *msg) { - if (peer->stfu) { + if (is_stfu_active(peer)) { msg_enqueue(peer->update_queue, take(msg)); return true; } @@ -353,6 +413,8 @@ static void set_channel_type(struct channel *channel, const u8 *type) * it's a private channel) */ static void send_channel_update(struct peer *peer, int disable_flag) { + status_debug("send_channel_update %d", disable_flag); + u8 *msg; assert(disable_flag == 0 || disable_flag == ROUTING_FLAGS_DISABLED); @@ -529,15 +591,22 @@ static void announce_channel(struct peer *peer) send_channel_update(peer, 0); } -static void channel_announcement_negotiate(struct peer *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; + return false; /* Can't do anything until funding is locked. */ if (!peer->channel_ready[LOCAL] || !peer->channel_ready[REMOTE]) - return; + 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; @@ -563,7 +632,7 @@ static void channel_announcement_negotiate(struct peer *peer) * - MUST NOT send the `announcement_signatures` message. */ if (!(peer->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)) - return; + return false; /* BOLT #7: * @@ -578,6 +647,7 @@ static void channel_announcement_negotiate(struct peer *peer) 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 @@ -597,12 +667,131 @@ static void channel_announcement_negotiate(struct peer *peer) time_from_sec(GOSSIP_ANNOUNCE_DELAY(peer->dev_fast_gossip)), announce_channel, peer)); } + + return sent_announcement; +} + +/* Call this method when splice_locked status are changed. If both sides have + * splice_locked'ed than this function consumes the `splice_locked_ready` values + * and considers the channel funding to be switched to the splice tx. */ +static void check_mutual_splice_locked(struct peer *peer) +{ + u8 *msg; + const char *error; + struct inflight *inflight; + + /* If both sides haven't `splice_locked` we're not ready */ + if (!peer->splice_state->locked_ready[LOCAL] + || !peer->splice_state->locked_ready[REMOTE]) + return; + + if (short_channel_id_eq(&peer->short_channel_ids[LOCAL], + &peer->splice_state->short_channel_id)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Duplicate splice_locked events detected"); + + peer->splice_state->await_commitment_succcess = true; + + /* This splice_locked event is used, so reset the flags to false */ + 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; + + peer->channel->view[LOCAL].lowest_splice_amnt[LOCAL] = 0; + peer->channel->view[LOCAL].lowest_splice_amnt[REMOTE] = 0; + peer->channel->view[REMOTE].lowest_splice_amnt[LOCAL] = 0; + peer->channel->view[REMOTE].lowest_splice_amnt[REMOTE] = 0; + + status_debug("mutual splice_locked, scid LOCAL & REMOTE updated to: %s", + type_to_string(tmpctx, struct short_channel_id, + &peer->splice_state->short_channel_id)); + + inflight = NULL; + for (size_t i = 0; i < tal_count(peer->splice_state->inflights); i++) + if (bitcoin_txid_eq(&peer->splice_state->inflights[i]->outpoint.txid, + &peer->splice_state->locked_txid)) + inflight = peer->splice_state->inflights[i]; + + if (!inflight) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to find inflight txid amoung %zu" + " inflights. new funding txid: %s", + tal_count(peer->splice_state->inflights), + type_to_string(tmpctx, struct bitcoin_txid, + &peer->splice_state->locked_txid)); + + status_debug("mutual splice_locked, updating change from: %s", + type_to_string(tmpctx, struct channel, peer->channel)); + + error = channel_update_funding(peer->channel, &inflight->outpoint, + inflight->amnt, + inflight->splice_amnt); + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice lock unable to update funding. %s", + error); + + status_debug("mutual splice_locked, channel updated to: %s", + type_to_string(tmpctx, struct channel, peer->channel)); + + msg = towire_channeld_got_splice_locked(NULL, inflight->amnt, + inflight->splice_amnt, + &inflight->outpoint.txid); + wire_sync_write(MASTER_FD, take(msg)); + + channel_announcement_negotiate(peer); + billboard_update(peer); + send_channel_update(peer, 0); + + /* valgrind complains last_tx is leaked so we explicitly free it + * DTODO: investigate this more. */ + for (size_t i = 0; i < tal_count(peer->splice_state->inflights); i++) { + inflight = peer->splice_state->inflights[i]; + tal_free(inflight->last_tx); + tal_free(inflight); + } + + peer->splice_state->inflights = tal_free(peer->splice_state->inflights); + peer->splice_state->count = 0; + peer->splice_state->revoked_count = 0; + peer->splice_state->committed_count = 0; +} + +/* Our peer told us they saw our splice confirm on chain with `splice_locked`. + * If we see it to we jump into tansitioning to post-splice, otherwise we mark + * a flag and wait until we see it on chain too. */ +static void handle_peer_splice_locked(struct peer *peer, const u8 *msg) +{ + struct channel_id chanid; + + if (!fromwire_splice_locked(msg, &chanid)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad splice_locked %s", tal_hex(msg, msg)); + + if (!channel_id_eq(&chanid, &peer->channel_id)) + peer_failed_err(peer->pps, &chanid, + "Wrong splice lock channel id in %s " + "(expected %s)", + tal_hex(tmpctx, msg), + type_to_string(msg, struct channel_id, + &peer->channel_id)); + + peer->splice_state->locked_ready[REMOTE] = true; + check_mutual_splice_locked(peer); } static void handle_peer_channel_ready(struct peer *peer, const u8 *msg) { struct channel_id chanid; struct tlv_channel_ready_tlvs *tlvs; + /* BOLT #2: * * A node: @@ -645,6 +834,7 @@ static void handle_peer_channel_ready(struct peer *peer, const u8 *msg) channel_announcement_negotiate(peer); billboard_update(peer); + peer->send_duplicate_announce_sigs = true; } /* Checks that key is valid, and signed this hash @@ -665,16 +855,34 @@ 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; if (!fromwire_announcement_signatures(msg, &chanid, - &peer->short_channel_ids[REMOTE], + &remote_scid, &peer->announcement_node_sigs[REMOTE], &peer->announcement_bitcoin_sigs[REMOTE])) 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[REMOTE]), + type_to_string(tmpctx, struct short_channel_id, + &peer->short_channel_ids[LOCAL])); + + 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, @@ -725,7 +933,12 @@ static void handle_peer_announcement_signatures(struct peer *peer, const u8 *msg peer->have_sigs[REMOTE] = true; billboard_update(peer); - channel_announcement_negotiate(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); + } } static void handle_peer_add_htlc(struct peer *peer, const u8 *msg) @@ -967,6 +1180,9 @@ static void maybe_send_shutdown(struct peer *peer) if (!peer->send_shutdown) return; + /* 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, ROUTING_FLAGS_DISABLED); @@ -1061,7 +1277,8 @@ static struct simple_htlc **collect_htlcs(const tal_t *ctx, const struct htlc ** return htlcs; } -/* Returns HTLC sigs, sets commit_sig */ +/* Returns HTLC sigs, sets commit_sig. Also used for making commitsigs for each + * splice awaiting on-chain confirmation. */ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, const struct peer *peer, struct bitcoin_tx **txs, @@ -1193,7 +1410,7 @@ static bool want_fee_update(const struct peer *peer, u32 *target) return false; /* No fee update while quiescing! */ - if (peer->stfu) + if (peer->want_stfu || is_stfu_active(peer)) return false; current = channel_feerate(peer->channel, REMOTE); @@ -1232,7 +1449,7 @@ static bool want_blockheight_update(const struct peer *peer, u32 *height) return false; /* No fee update while quiescing! */ - if (peer->stfu) + if (peer->want_stfu || is_stfu_active(peer)) return false; /* What's the current blockheight */ @@ -1254,19 +1471,97 @@ static bool want_blockheight_update(const struct peer *peer, u32 *height) return true; } -static void send_commit(struct peer *peer) +static u8 *send_commit_part(struct peer *peer, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + const struct htlc **changed_htlcs, + bool notify_master, + s64 splice_amnt, + s64 remote_splice_amnt) { u8 *msg; - const struct htlc **changed_htlcs; struct bitcoin_signature commit_sig, *htlc_sigs; struct bitcoin_tx **txs; const u8 *funding_wscript; const struct htlc **htlc_map; struct wally_tx_output *direct_outputs[NUM_SIDES]; struct penalty_base *pbase; + + status_debug("send_commit_part(splice: %d, remote_splice: %d)", + (int)splice_amnt, (int)remote_splice_amnt); + + + struct tlv_commitment_signed_tlvs *cs_tlv + = tlv_commitment_signed_tlvs_new(tmpctx); + cs_tlv->splice_info = tal(cs_tlv, struct channel_id); + derive_channel_id(cs_tlv->splice_info, funding); + + txs = channel_splice_txs(tmpctx, funding, funding_sats, &htlc_map, + direct_outputs, &funding_wscript, + peer->channel, &peer->remote_per_commit, + peer->next_index[REMOTE], REMOTE, + splice_amnt, remote_splice_amnt); + htlc_sigs = + calc_commitsigs(tmpctx, peer, txs, funding_wscript, htlc_map, + peer->next_index[REMOTE], &commit_sig); + + if (direct_outputs[LOCAL] != NULL) { + pbase = penalty_base_new(tmpctx, peer->next_index[REMOTE], + txs[0], direct_outputs[LOCAL]); + + /* Add the penalty_base to our in-memory list as well, so we + * can find it again later. */ + tal_arr_expand(&peer->pbases, tal_steal(peer, pbase)); + } else + pbase = NULL; + +#if DEVELOPER + if (peer->dev_disable_commit) { + (*peer->dev_disable_commit)--; + if (*peer->dev_disable_commit == 0) + status_unusual("dev-disable-commit-after: disabling"); + } +#endif + + if (notify_master) { + status_debug("Telling master we're about to commit..."); + /* Tell master to save this next commit to database, then wait. + */ + msg = sending_commitsig_msg(NULL, peer->next_index[REMOTE], + pbase, + peer->channel->fee_states, + peer->channel->blockheight_states, + changed_htlcs, + &commit_sig, + htlc_sigs); + /* Message is empty; receiving it is the point. */ + master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_SENDING_COMMITSIG_REPLY); + + status_debug("Sending commit_sig with %zu htlc sigs", + tal_count(htlc_sigs)); + } + + msg = towire_commitment_signed(NULL, &peer->channel_id, + &commit_sig.s, + raw_sigs(tmpctx, htlc_sigs), + cs_tlv); + return msg; +} + +/* unlike amount.h, we expect negative values for a - b. */ +static s64 sats_diff(struct amount_sat a, struct amount_sat b) +{ + return (s64)a.satoshis - (s64)b.satoshis; /* Raw: splicing numbers can wrap! */ +} + +static void send_commit(struct peer *peer) +{ + const struct htlc **changed_htlcs; u32 our_blockheight; u32 feerate_target; - + u8 **msgs = tal_arr(tmpctx, u8*, 1); + u8 *msg; #if DEVELOPER if (peer->dev_disable_commit && !*peer->dev_disable_commit) { peer->commit_timer = NULL; @@ -1360,7 +1655,10 @@ static void send_commit(struct peer *peer) * any updates. */ changed_htlcs = tal_arr(tmpctx, const struct htlc *, 0); - if (!channel_sending_commit(peer->channel, &changed_htlcs)) { + + if (peer->splice_state->committed_count == peer->splice_state->count + && !channel_sending_commit(peer->channel, &changed_htlcs)) { + status_debug("Can't send commit: nothing to send," " feechange %s (%s)" " blockheight %s (%s)", @@ -1376,54 +1674,40 @@ static void send_commit(struct peer *peer) return; } - txs = channel_txs(tmpctx, &htlc_map, direct_outputs, - &funding_wscript, peer->channel, &peer->remote_per_commit, - peer->next_index[REMOTE], REMOTE); - - htlc_sigs = - calc_commitsigs(tmpctx, peer, txs, funding_wscript, htlc_map, - peer->next_index[REMOTE], &commit_sig); - - if (direct_outputs[LOCAL] != NULL) { - pbase = penalty_base_new(tmpctx, peer->next_index[REMOTE], - txs[0], direct_outputs[LOCAL]); - - /* Add the penalty_base to our in-memory list as well, so we - * can find it again later. */ - tal_arr_expand(&peer->pbases, tal_steal(peer, pbase)); - } else - pbase = NULL; + msgs[0] = send_commit_part(peer, &peer->channel->funding, + peer->channel->funding_sats, changed_htlcs, + true, 0, 0); -#if DEVELOPER - if (peer->dev_disable_commit) { - (*peer->dev_disable_commit)--; - if (*peer->dev_disable_commit == 0) - status_unusual("dev-disable-commit-after: disabling"); + /* Loop over current inflights + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * + * A sending node: + *... + * - MUST first send a `commitment_signed` for the active channel then immediately + * send a `commitment_signed` for each splice awaiting confirmation, in increasing + * feerate order. + */ + for (u32 i = 0; i < tal_count(peer->splice_state->inflights); i++) { + s64 funding_diff = sats_diff(peer->splice_state->inflights[i]->amnt, + peer->channel->funding_sats); + s64 remote_splice_amnt = funding_diff + - peer->splice_state->inflights[i]->splice_amnt; + + tal_arr_expand(&msgs, + send_commit_part(peer, + &peer->splice_state->inflights[i]->outpoint, + peer->splice_state->inflights[i]->amnt, + changed_htlcs, false, + peer->splice_state->inflights[i]->splice_amnt, + remote_splice_amnt)); } -#endif - - status_debug("Telling master we're about to commit..."); - /* Tell master to save this next commit to database, then wait. */ - msg = sending_commitsig_msg(NULL, peer->next_index[REMOTE], - pbase, - peer->channel->fee_states, - peer->channel->blockheight_states, - changed_htlcs, - &commit_sig, - htlc_sigs); - /* Message is empty; receiving it is the point. */ - master_wait_sync_reply(tmpctx, peer, take(msg), - WIRE_CHANNELD_SENDING_COMMITSIG_REPLY); - - status_debug("Sending commit_sig with %zu htlc sigs", - tal_count(htlc_sigs)); peer->next_index[REMOTE]++; - msg = towire_commitment_signed(NULL, &peer->channel_id, - &commit_sig.s, - raw_sigs(tmpctx, htlc_sigs)); - peer_write(peer->pps, take(msg)); + for(u32 i = 0; i < tal_count(msgs); i++) + peer_write(peer->pps, take(msgs[i])); + + peer->splice_state->committed_count = peer->splice_state->count; maybe_send_shutdown(peer); @@ -1550,7 +1834,8 @@ static void send_revocation(struct peer *peer, const struct htlc **changed_htlcs, const struct bitcoin_tx *committx, const struct secret *old_secret, - const struct pubkey *next_point) + const struct pubkey *next_point, + const struct commitsig **splice_commitsigs) { struct changed_htlc *changed; struct fulfilled_htlc *fulfilled; @@ -1596,42 +1881,102 @@ static void send_revocation(struct peer *peer, fulfilled, failed, changed, - committx); + committx, + splice_commitsigs); master_wait_sync_reply(tmpctx, peer, take(msg_for_master), WIRE_CHANNELD_GOT_COMMITSIG_REPLY); + peer->splice_state->await_commitment_succcess = false; + /* Now we can finally send revoke_and_ack to peer */ peer_write(peer->pps, take(msg)); } -static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) +/* Calling `handle_peer_commit_sig` with a `commit_index` of 0 and + * `changed_htlcs` of NULL will process the message, then read & process coming + * consecutive commitment messages equal to the number of inflight splices. + * + * Returns the last commitsig received. When splicing this is the + * newest splice commit sig. */ +static struct commitsig *handle_peer_commit_sig(struct peer *peer, + const u8 *msg, + u32 commit_index, + const struct htlc **changed_htlcs, + s64 splice_amnt, + s64 remote_splice_amnt) { + struct commitsig *result; struct channel_id channel_id; struct bitcoin_signature commit_sig; secp256k1_ecdsa_signature *raw_sigs; struct bitcoin_signature *htlc_sigs; struct pubkey remote_htlckey; struct bitcoin_tx **txs; - const struct htlc **htlc_map, **changed_htlcs; + const struct htlc **htlc_map; const u8 *funding_wscript; size_t i; struct simple_htlc **htlcs; const u8 * msg2; + u8 *splice_msg; + int type; + struct bitcoin_outpoint outpoint; + struct amount_sat funding_sats; + struct channel_id active_id; + const struct commitsig **commitsigs; - changed_htlcs = tal_arr(msg, const struct htlc *, 0); - if (!channel_rcvd_commit(peer->channel, &changed_htlcs)) { - /* BOLT #2: - * - * A sending node: - * - MUST NOT send a `commitment_signed` message that does not - * include any updates. - */ - status_debug("Oh hi LND! Empty commitment at #%"PRIu64, - peer->next_index[LOCAL]); - if (peer->last_empty_commitment == peer->next_index[LOCAL] - 1) - peer_failed_warn(peer->pps, &peer->channel_id, - "commit_sig with no changes (again!)"); - peer->last_empty_commitment = peer->next_index[LOCAL]; + status_debug("handle_peer_commit_sig(splice: %d, remote_splice: %d)", + (int)splice_amnt, (int)remote_splice_amnt); + + struct tlv_commitment_signed_tlvs *cs_tlv + = tlv_commitment_signed_tlvs_new(tmpctx); + if (!fromwire_commitment_signed(tmpctx, msg, + &channel_id, &commit_sig.s, &raw_sigs, + &cs_tlv)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad commit_sig %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 `commitment_signed` messages where `splice_channel_id` + * does not match the `channel_id` of the confirmed splice. */ + derive_channel_id(&active_id, &peer->channel->funding); + if (peer->splice_state->await_commitment_succcess + && !tal_count(peer->splice_state->inflights) && cs_tlv && cs_tlv->splice_info) { + if (!channel_id_eq(&active_id, cs_tlv->splice_info)) { + status_info("Ignoring stale commit_sig for channel_id" + " %s, as %s is locked in now.", + type_to_string(tmpctx, struct channel_id, + cs_tlv->splice_info), + type_to_string(tmpctx, struct channel_id, + &active_id)); + return NULL; + } + } + + /* In a race we can get here with a commitsig with too many splices + * attached. In that case we ignore the main commit msg for the old + * funding tx, and for the splice candidates that didnt win. But we must + * listen to the one that is for the winning splice candidate */ + + if (!changed_htlcs) { + changed_htlcs = tal_arr(msg, const struct htlc *, 0); + if (!channel_rcvd_commit(peer->channel, &changed_htlcs) + && peer->splice_state->count == peer->splice_state->revoked_count) { + /* BOLT #2: + * + * A sending node: + * - MUST NOT send a `commitment_signed` message that does not + * include any updates. + */ + status_debug("Oh hi LND! Empty commitment at #%"PRIu64, + peer->next_index[LOCAL]); + if (peer->last_empty_commitment == peer->next_index[LOCAL] - 1) + peer_failed_warn(peer->pps, &peer->channel_id, + "commit_sig with no changes (again!)"); + peer->last_empty_commitment = peer->next_index[LOCAL]; + } } /* We were supposed to check this was affordable as we go. */ @@ -1644,19 +1989,25 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) LOCAL))); } - if (!fromwire_commitment_signed(tmpctx, msg, - &channel_id, &commit_sig.s, &raw_sigs)) - peer_failed_warn(peer->pps, &peer->channel_id, - "Bad commit_sig %s", tal_hex(msg, msg)); /* SIGHASH_ALL is implied. */ commit_sig.sighash_type = SIGHASH_ALL; htlc_sigs = unraw_sigs(tmpctx, raw_sigs, channel_has_anchors(peer->channel)); - txs = - channel_txs(tmpctx, &htlc_map, NULL, - &funding_wscript, peer->channel, &peer->next_local_per_commit, - peer->next_index[LOCAL], LOCAL); + if (commit_index) { + outpoint = peer->splice_state->inflights[commit_index - 1]->outpoint; + funding_sats = peer->splice_state->inflights[commit_index - 1]->amnt; + } + else { + outpoint = peer->channel->funding; + funding_sats = peer->channel->funding_sats; + } + + txs = channel_splice_txs(tmpctx, &outpoint, funding_sats, &htlc_map, + NULL, &funding_wscript, peer->channel, + &peer->next_local_per_commit, + peer->next_index[LOCAL], LOCAL, splice_amnt, + remote_splice_amnt); /* Set the commit_sig on the commitment tx psbt */ if (!psbt_input_set_signature(txs[0]->psbt, 0, @@ -1688,7 +2039,10 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) &peer->channel->funding_pubkey[REMOTE], &commit_sig)) { dump_htlcs(peer->channel, "receiving commit_sig"); peer_failed_warn(peer->pps, &peer->channel_id, - "Bad commit_sig signature %"PRIu64" %s for tx %s wscript %s key %s feerate %u", + "Bad commit_sig signature %"PRIu64" %s for tx" + " %s wscript %s key %s feerate %u. Cur funding" + " %s, splice_info: %s, race_await_commit: %s," + " inflight splice count: %zu", peer->next_index[LOCAL], type_to_string(msg, struct bitcoin_signature, &commit_sig), @@ -1697,7 +2051,15 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) type_to_string(msg, struct pubkey, &peer->channel->funding_pubkey [REMOTE]), - channel_feerate(peer->channel, LOCAL)); + channel_feerate(peer->channel, LOCAL), + type_to_string(tmpctx, struct channel_id, + &active_id), + type_to_string(tmpctx, struct channel_id, + (cs_tlv ? cs_tlv->splice_info + : NULL)), + peer->splice_state->await_commitment_succcess ? "yes" + : "no", + tal_count(peer->splice_state->inflights)); } /* BOLT #2: @@ -1741,6 +2103,19 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) status_debug("Received commit_sig with %zu htlc sigs", tal_count(htlc_sigs)); + /* First pass some common error scenarios for nicer log outputs */ + if (peer->splice_state->count) { + if (!cs_tlv) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad commitment_signed mesage" + " without a splice commit sig" + " section during a splice."); + if (tal_count(peer->splice_state->inflights) != peer->splice_state->count) + peer_failed_warn(peer->pps, &peer->channel_id, + "Internal splice inflight counting " + "error"); + } + /* Validate the counterparty's signatures, returns prior per_commitment_secret. */ htlcs = collect_htlcs(NULL, htlc_map); msg2 = towire_hsmd_validate_commitment_tx(NULL, @@ -1759,16 +2134,62 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) "Reading validate_commitment_tx reply: %s", tal_hex(tmpctx, msg2)); + result = tal(tmpctx, struct commitsig); + result->tx = clone_bitcoin_tx(result, txs[0]); + result->commit_signature = commit_sig; + result->htlc_signatures = htlc_sigs; + + /* Only the parent call continues from here. + * Return for all child calls. */ + if(commit_index) + return result; + + commitsigs = tal_arr(NULL, const struct commitsig*, 0); + /* We expect multiple consequtive commit_sig messages if we have + * inflight splices. Since consequtive is requred, we recurse for + * each expected message, blocking until all are received. */ + for (i = 0; i < tal_count(peer->splice_state->inflights); i++) { + s64 funding_diff = sats_diff(peer->splice_state->inflights[i]->amnt, + peer->channel->funding_sats); + s64 sub_splice_amnt = peer->splice_state->inflights[i]->splice_amnt; + + splice_msg = peer_read(tmpctx, peer->pps); + /* Check type for cleaner failure message */ + type = fromwire_peektype(msg); + if (type != WIRE_COMMITMENT_SIGNED) + peer_failed_err(peer->pps, &peer->channel_id, + "Expected splice related " + "WIRE_COMMITMENT_SIGNED but got %s", + peer_wire_name(type)); + + /* We purposely just store the last commit msg */ + result = handle_peer_commit_sig(peer, splice_msg, i + 1, + changed_htlcs, sub_splice_amnt, + funding_diff - sub_splice_amnt); + tal_arr_expand(&commitsigs, result); + tal_steal(commitsigs, result); + } + + peer->splice_state->revoked_count = peer->splice_state->count; + send_revocation(peer, &commit_sig, htlc_sigs, changed_htlcs, txs[0], - old_secret, &next_point); + old_secret, &next_point, commitsigs); + + tal_steal(tmpctx, result); + tal_free(commitsigs); - /* We may now be quiescent on our side. */ + /* STFU can't be activated during pending updates. + * With updates finish let's handle a potentially queued stfu request. + */ maybe_send_stfu(peer); /* This might have synced the feerates: if so, we may want to * update */ if (want_fee_update(peer, NULL)) start_commit_timer(peer); + + /* We return the last commit commit msg */ + return result; } /* Pops the penalty base for the given commitnum from our internal list. There @@ -1816,6 +2237,7 @@ static u8 *got_revoke_msg(struct peer *peer, u64 revoke_num, pbase = penalty_base_by_commitnum(tmpctx, peer, revoke_num); if (pbase) { + /* DTODO we need penalty tx's per splice candidate */ ptx = penalty_tx_create( NULL, peer->channel, peer->feerate_penalty, peer->final_index, peer->final_ext_key, @@ -1912,7 +2334,11 @@ static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg) type_to_string(tmpctx, struct pubkey, &peer->old_remote_per_commit)); - /* We may now be quiescent on our side. */ + peer->splice_state->await_commitment_succcess = false; + + /* STFU can't be activated during pending updates. + * With updates finish let's handle a potentially queued stfu request. + */ maybe_send_stfu(peer); start_commit_timer(peer); @@ -2054,6 +2480,9 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) struct tlv_shutdown_tlvs *tlvs; struct bitcoin_outpoint *wrong_funding; + /* DTODO: Ensure `shutdown` follows new splice related rules once + * completed in the spec */ + /* Disable the channel. */ send_channel_update(peer, ROUTING_FLAGS_DISABLED); @@ -2150,16 +2579,19 @@ static void handle_unexpected_tx_sigs(struct peer *peer, const u8 *msg) struct channel_id cid; struct bitcoin_txid txid; + struct tlv_txsigs_tlvs *txsig_tlvs = tlv_txsigs_tlvs_new(tmpctx); + /* In a rare case, a v2 peer may re-send a tx_sigs message. * This happens when they've/we've exchanged channel_ready, * but they did not receive our channel_ready. */ if (!fromwire_tx_signatures(tmpctx, msg, &cid, &txid, - cast_const3(struct witness ***, &witnesses))) + cast_const3(struct witness ***, &witnesses), + &txsig_tlvs)) peer_failed_warn(peer->pps, &peer->channel_id, "Bad tx_signatures %s", tal_hex(msg, msg)); - status_info("Unexpected `tx_signatures` from peer. %s", + status_info("Unexpected `tx_signatures` from peer-> %s", peer->tx_sigs_allowed ? "Allowing." : "Failing."); if (!peer->tx_sigs_allowed) @@ -2225,65 +2657,1485 @@ static void handle_unexpected_reestablish(struct peer *peer, const u8 *msg) &channel_id)); } -static void peer_in(struct peer *peer, const u8 *msg) +static bool is_initiators_serial(const struct wally_map *unknowns) { - enum peer_wire type = fromwire_peektype(msg); + /* BOLT-f15b6b0feeffc2acd1a8466537810bbb3f824f9f #2: + * The sending node: ... + * - if is the *initiator*: + * - MUST send even `serial_id`s + * - if is the *non-initiator*: + * - MUST send odd `serial_id`s + */ + u64 serial_id; + if (!psbt_get_serial_id(unknowns, &serial_id)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "PSBTs must have serial_ids set"); - if (handle_peer_error(peer->pps, &peer->channel_id, msg)) - return; + return serial_id % 2 == TX_INITIATOR; +} - /* Must get channel_ready before almost anything. */ - if (!peer->channel_ready[REMOTE]) { - if (type != WIRE_CHANNEL_READY - && type != WIRE_SHUTDOWN - /* We expect these for v2 !! */ - && type != WIRE_TX_SIGNATURES - /* lnd sends these early; it's harmless. */ - && type != WIRE_UPDATE_FEE - && type != WIRE_ANNOUNCEMENT_SIGNATURES) { - peer_failed_warn(peer->pps, &peer->channel_id, - "%s (%u) before funding locked", - peer_wire_name(type), type); - } - } +static void add_amount_to_side(struct peer *peer, + struct amount_msat amounts[NUM_TX_ROLES], + const struct amount_sat amount, + const struct wally_map *unknowns) +{ + enum tx_role role; - switch (type) { - case WIRE_CHANNEL_READY: - handle_peer_channel_ready(peer, msg); - return; - case WIRE_ANNOUNCEMENT_SIGNATURES: - handle_peer_announcement_signatures(peer, msg); - return; - case WIRE_UPDATE_ADD_HTLC: - handle_peer_add_htlc(peer, msg); - return; - case WIRE_COMMITMENT_SIGNED: - handle_peer_commit_sig(peer, msg); - return; - case WIRE_UPDATE_FEE: - handle_peer_feechange(peer, msg); - return; - case WIRE_UPDATE_BLOCKHEIGHT: - handle_peer_blockheight_change(peer, msg); - return; - case WIRE_REVOKE_AND_ACK: - handle_peer_revoke_and_ack(peer, msg); - return; - case WIRE_UPDATE_FULFILL_HTLC: - handle_peer_fulfill_htlc(peer, msg); - return; - case WIRE_UPDATE_FAIL_HTLC: - handle_peer_fail_htlc(peer, msg); - return; - case WIRE_UPDATE_FAIL_MALFORMED_HTLC: - handle_peer_fail_malformed_htlc(peer, msg); - return; - case WIRE_SHUTDOWN: - handle_peer_shutdown(peer, msg); - return; - case WIRE_STFU: + if (amount_sat_zero(amount)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add 0 sat fields to calculation"); + + if(is_initiators_serial(unknowns)) + role = TX_INITIATOR; + else + role = TX_ACCEPTER; + + if (!amount_msat_add_sat(&amounts[role], amounts[role], amount)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add field amount %s to rolling" + " total %s", + fmt_amount_sat(tmpctx, amount), + fmt_amount_msat(tmpctx, amounts[role])); +} + +static bool do_i_sign_first(struct peer *peer, struct wally_psbt *psbt, + enum tx_role our_role) +{ + struct amount_msat in[NUM_TX_ROLES]; + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * - MAY send `tx_signatures` first. */ + if (peer->splice->force_sign_first) + return true; + + in[TX_INITIATOR] = AMOUNT_MSAT(0); + in[TX_ACCEPTER] = AMOUNT_MSAT(0); + + for (size_t i = 0; i < psbt->num_inputs; i++) + add_amount_to_side(peer, in, psbt_input_get_amount(psbt, i), + &psbt->inputs[i].unknowns); + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * - If recipient's sum(tx_add_input.amount) < peer's + * sum(tx_add_input.amount); or if recipient's + * sum(tx_add_input.amount) == peer's sum(tx_add_input.amount) and + * recipient is the `initiator` of the splice: + * - SHOULD send `tx_signatures` first for the splice transaction. */ + if (amount_msat_less(in[TX_ACCEPTER], in[TX_INITIATOR])) + return our_role == TX_ACCEPTER; + + if (amount_msat_less(in[TX_INITIATOR], in[TX_ACCEPTER])) + return our_role == TX_INITIATOR; + + return our_role == TX_INITIATOR; +} + +static struct wally_psbt *next_splice_step(const tal_t *ctx, + struct interactivetx_context *ictx) +{ + /* DTODO: add plugin wrapper for accepter side of splice to add to the + * negotiated splice. */ + if (ictx->our_role == TX_ACCEPTER) + return NULL; + + return ictx->desired_psbt; +} + +static const u8 *peer_expect_msg(const tal_t *ctx, + struct peer *peer, + enum peer_wire expect_type, + enum peer_wire second_allowed_type) +{ + u8 *msg; + enum peer_wire type; + + msg = peer_read(ctx, peer->pps); + type = fromwire_peektype(msg); + if (type != expect_type && type != second_allowed_type) + peer_failed_warn(peer->pps, &peer->channel_id, + "Got incorrect message from peer: %s" + " (should be %s) [%s]", + peer_wire_name(type), + peer_wire_name(expect_type), + sanitize_error(tmpctx, msg, &peer->channel_id)); + + return msg; +} + +/* The question of "who signs splice commitments first" is the same order as the + * splice `tx_signature`s are. This function handles sending & receiving the + * required commitments as part of the splicing process. */ +static struct commitsig *interactive_send_commitments(struct peer *peer, + struct wally_psbt *psbt, + enum tx_role our_role) +{ + struct commitsig *result; + const u8 *msg, *commit_msg; + + commit_msg = NULL; + + if (do_i_sign_first(peer, psbt, our_role)) { + + status_debug("Splice %s: we commit first", + our_role == TX_INITIATOR ? "initiator" : "accepter"); + + send_commit(peer); + + /* If both sides commit simultaneously, that's fine. */ + msg = peer_expect_msg(tmpctx, peer, WIRE_REVOKE_AND_ACK, + WIRE_COMMITMENT_SIGNED); + + /* If commitments happened simultaneously, we'll get a + * commitment signed message, followed by revoke and ack. + */ + if (fromwire_peektype(msg) == WIRE_COMMITMENT_SIGNED) { + commit_msg = msg; + result = handle_peer_commit_sig(peer, commit_msg, 0, + NULL, 0, 0); + + msg = peer_expect_msg(tmpctx, peer, + WIRE_REVOKE_AND_ACK, 0); + } + + handle_peer_revoke_and_ack(peer, msg); + } + + if (!commit_msg) { + commit_msg = peer_expect_msg(tmpctx, peer, + WIRE_COMMITMENT_SIGNED, 0); + result = handle_peer_commit_sig(peer, commit_msg, 0, + NULL, 0, 0); + } + + if (!do_i_sign_first(peer, psbt, our_role)) { + + status_debug("Splice %s: we commit second", + our_role == TX_INITIATOR ? "initiator" : "accepter"); + + send_commit(peer); + + msg = peer_expect_msg(tmpctx, peer, + WIRE_REVOKE_AND_ACK, 0); + + handle_peer_revoke_and_ack(peer, msg); + } + + return result; +} + +static struct wally_psbt_output *find_channel_output(struct peer *peer, + struct wally_psbt *psbt, + u32 *chan_output_index) +{ + const u8 *wit_script; + u8 *scriptpubkey; + + wit_script = bitcoin_redeem_2of2(tmpctx, + &peer->channel->funding_pubkey[LOCAL], + &peer->channel->funding_pubkey[REMOTE]); + + scriptpubkey = scriptpubkey_p2wsh(psbt, wit_script); + + for (size_t i = 0; i < psbt->num_outputs; i++) { + if (memeq(psbt->outputs[i].script, + psbt->outputs[i].script_len, + scriptpubkey, + tal_bytelen(scriptpubkey))) { + if (chan_output_index) + *chan_output_index = i; + return &psbt->outputs[i]; + } + } + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to find channel output"); + return NULL; +} + +static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt) +{ + size_t weight = 0; + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The initiator: + * ... + * - MUST pay for the common fields. + */ + if (role == TX_INITIATOR) + weight += bitcoin_tx_core_weight(psbt->num_inputs, + psbt->num_outputs); + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * Each node: + * - MUST pay for their own added inputs and outputs. + */ + for (size_t i = 0; i < psbt->num_inputs; i++) + if (is_initiators_serial(&psbt->inputs[i].unknowns)) { + if (role == TX_INITIATOR) + weight += psbt_input_get_weight(psbt, i); + } + else + if (role != TX_INITIATOR) + weight += psbt_input_get_weight(psbt, i); + + for (size_t i = 0; i < psbt->num_outputs; i++) + if (is_initiators_serial(&psbt->inputs[i].unknowns)) { + if (role == TX_INITIATOR) + weight += psbt_output_get_weight(psbt, i); + } + else + if (role != TX_INITIATOR) + weight += psbt_output_get_weight(psbt, i); + + return weight; +} + +/* Returns the total channel funding output amount if all checks pass. + * Otherwise, exits via peer_failed_warn. DTODO: Change to `tx_abort`. */ +static struct amount_sat check_balances(struct peer *peer, + enum tx_role our_role, + const struct wally_psbt *psbt, + int chan_output_index, + int chan_input_index) +{ + struct amount_sat min_initiator_fee, min_accepter_fee, + max_initiator_fee, max_accepter_fee, + funding_amount_res; + struct amount_msat funding_amount, + initiator_fee, accepter_fee; + struct amount_msat in[NUM_TX_ROLES], out[NUM_TX_ROLES]; + bool opener = our_role == TX_INITIATOR; + u8 *msg; + + in[TX_INITIATOR] = peer->channel->view->owed[opener ? LOCAL : REMOTE]; + in[TX_ACCEPTER] = peer->channel->view->owed[opener ? REMOTE : LOCAL]; + + for (size_t i = 0; i < psbt->num_inputs; i++) + if (i != chan_input_index) + add_amount_to_side(peer, in, + psbt_input_get_amount(psbt, i), + &psbt->inputs[i].unknowns); + + /* The outgoing channel funds start as current funds, will be modified + * by the splice amount later on */ + out[TX_INITIATOR] = peer->channel->view->owed[opener ? LOCAL : REMOTE]; + out[TX_ACCEPTER] = peer->channel->view->owed[opener ? REMOTE : LOCAL]; + + for (size_t i = 0; i < psbt->num_outputs; i++) + if (i != chan_output_index) + add_amount_to_side(peer, out, + psbt_output_get_amount(psbt, i), + &psbt->outputs[i].unknowns); + + /* Calculate total channel output amount */ + if (!amount_msat_add(&funding_amount, + peer->channel->view->owed[LOCAL], + peer->channel->view->owed[REMOTE])) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to calculate starting channel amount"); + + /* Tasks: + * Add up total funding_amount + * Check in[TX_INITIATOR] - out[TX_INITIATOR] > opener_relative + * - refactor as in[TX_INITIATOR] > opener_relative + out[TX_INITIATOR] + * - remainder is the fee contribution + * Check in[TX_ACCEPTER] - out[TX_ACCEPTER] > accepter_relative + * - refactor as in[TX_INITIATOR] > opener_relative + out[TX_INITIATOR] + * - remainder is the fee contribution + * + * Check if fee rate is too low anywhere + * Check if fee rate is too high locally + * + * While we're, here, adjust the output counts by splice amount. + */ + + if(peer->splice->opener_relative > 0) { + if (!amount_msat_add_sat(&funding_amount, funding_amount, + amount_sat((u64)peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add opener funding"); + if (!amount_msat_add_sat(&out[TX_INITIATOR], out[TX_INITIATOR], + amount_sat((u64)peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add opener funding to out amnt."); + } else { + if (!amount_msat_sub_sat(&funding_amount, funding_amount, + amount_sat((u64)-peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to sub opener funding"); + if (!amount_msat_sub_sat(&out[TX_INITIATOR], out[TX_INITIATOR], + amount_sat((u64)peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to sub opener funding from out amnt."); + } + + if(peer->splice->accepter_relative > 0) { + if (!amount_msat_add_sat(&funding_amount, funding_amount, + amount_sat((u64)peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add accepter funding"); + if (!amount_msat_add_sat(&out[TX_ACCEPTER], out[TX_ACCEPTER], + amount_sat((u64)peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add accepter funding to out amnt."); + } else { + if (!amount_msat_sub_sat(&funding_amount, funding_amount, + amount_sat((u64)-peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to subtract accepter funding"); + if (!amount_msat_sub_sat(&out[TX_ACCEPTER], out[TX_ACCEPTER], + amount_sat((u64)-peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to sub accepter funding from out amnt."); + } + + if (amount_msat_less(in[TX_INITIATOR], out[TX_INITIATOR])) { + msg = towire_channeld_splice_funding_error(NULL, in[TX_INITIATOR], + out[TX_INITIATOR], + true); + wire_sync_write(MASTER_FD, take(msg)); + peer_failed_warn(peer->pps, &peer->channel_id, + "Initiator funding is less than commited" + " amount. Initiator contributing %s but they" + " committed to %s.", + fmt_amount_msat(tmpctx, in[TX_INITIATOR]), + fmt_amount_msat(tmpctx, out[TX_INITIATOR])); + } + + if (!amount_msat_sub(&initiator_fee, in[TX_INITIATOR], out[TX_INITIATOR])) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "amount_sat_less / amount_sat_sub mismtach"); + + if (amount_msat_less(in[TX_ACCEPTER], out[TX_ACCEPTER])) { + msg = towire_channeld_splice_funding_error(NULL, in[TX_INITIATOR], + out[TX_INITIATOR], + true); + wire_sync_write(MASTER_FD, take(msg)); + peer_failed_warn(peer->pps, &peer->channel_id, + "Accepter funding is less than commited" + " amount. Accepter contributing %s but they" + " committed to %s.", + fmt_amount_msat(tmpctx, in[TX_INITIATOR]), + fmt_amount_msat(tmpctx, out[TX_INITIATOR])); + } + + if (!amount_msat_sub(&accepter_fee, in[TX_ACCEPTER], out[TX_ACCEPTER])) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "amount_sat_less / amount_sat_sub mismtach"); + + min_initiator_fee = amount_tx_fee(peer->splice->feerate_per_kw, + calc_weight(TX_INITIATOR, psbt)); + min_accepter_fee = amount_tx_fee(peer->splice->feerate_per_kw, + calc_weight(TX_ACCEPTER, psbt)); + + /* As a safeguard max feerate is checked (only) locally, if it's + * particularly high we fail and tell the user but allow them to + * override with `splice_force_feerate` */ + max_accepter_fee = amount_tx_fee(peer->feerate_max, + calc_weight(TX_ACCEPTER, psbt)); + max_initiator_fee = amount_tx_fee(peer->feerate_max, + calc_weight(TX_INITIATOR, psbt)); + + /* Check initiator fee */ + if (amount_msat_less_sat(initiator_fee, min_initiator_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, initiator_fee, + false); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort`? */ + peer_failed_warn(peer->pps, &peer->channel_id, + "%s fee (%s) was too low, must be at least %s", + opener ? "Our" : "Your", + type_to_string(tmpctx, struct amount_msat, + &initiator_fee), + type_to_string(tmpctx, struct amount_sat, + &min_initiator_fee)); + } + if (!peer->splice->force_feerate && opener + && amount_msat_greater_sat(initiator_fee, max_initiator_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, initiator_fee, + true); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort` */ + peer_failed_warn(peer->pps, &peer->channel_id, + "Our own fee (%s) was too high, max without" + " forcing is %s.", + type_to_string(tmpctx, struct amount_msat, + &initiator_fee), + type_to_string(tmpctx, struct amount_sat, + &max_initiator_fee)); + } + /* Check accepter fee */ + if (amount_msat_less_sat(accepter_fee, min_accepter_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, accepter_fee, + false); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort`? */ + peer_failed_warn(peer->pps, &peer->channel_id, + "%s fee (%s) was too low, must be at least %s", + opener ? "Your" : "Our", + type_to_string(tmpctx, struct amount_msat, + &accepter_fee), + type_to_string(tmpctx, struct amount_sat, + &min_accepter_fee)); + } + if (!peer->splice->force_feerate && !opener + && amount_msat_greater_sat(accepter_fee, max_accepter_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, accepter_fee, + true); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort` */ + peer_failed_warn(peer->pps, &peer->channel_id, + "Our own fee (%s) was too high, max without" + " forcing is %s.", + type_to_string(tmpctx, struct amount_msat, + &accepter_fee), + type_to_string(tmpctx, struct amount_sat, + &max_accepter_fee)); + } + + /* BOLT-??? #2: + * - if either side has added an output other than the new channel + * funding output: + * - MUST fail the negotiation if the balance for that side is less + * than 1% of the total channel capacity. */ + /* DTODO: Spec out reserve requirements for splices!! Lets gooo */ + /* DTODO: If we were at or over the reserve at start of splice, + * then we must ensure the reserve is preserved through splice. + * It should only to 1% of the old balance + * 1: The channel is growing + * --- your balnce was underneath reserve req + * Valid: YES + * 2: The node's balance is shrinking + * --- and it shrinks below the reserve + * Valid: NO + * + * The reserve requirement should only matter if someone is withdrawing + * from. + * + * Node A Node B + * 1000 sat <-> 1000 sat + * reserve: 20sat + * + * Node B desires withdraw 990 sats + * Can I? + * New reserve req = 1010 * 0.01 = 10 (round down from 10.1) + * */ + + if (!amount_msat_to_sat(&funding_amount_res, funding_amount)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "splice error: msat of boths sides should always" + " add up to a full sat."); + + return funding_amount_res; +} + +static u32 find_channel_funding_input(struct wally_psbt *psbt, + struct bitcoin_outpoint *funding) +{ + for (size_t i = 0; i < psbt->num_inputs; i++) { + struct bitcoin_outpoint psbt_outpoint; + wally_psbt_input_get_outpoint(&psbt->inputs[i], &psbt_outpoint); + + if (!bitcoin_outpoint_eq(&psbt_outpoint, funding)) + continue; + + if (funding->n == psbt->inputs[i].index) + return i; + } + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to find splice funding tx"); + + return UINT_MAX; +} + +static void update_view_from_inflights(struct peer *peer) +{ + struct inflight **inflights = peer->splice_state->inflights; + + for (size_t i = 0; i < tal_count(inflights); i++) { + s64 splice_amnt = inflights[i]->amnt.satoshis; /* Raw: splicing */ + s64 funding_diff = sats_diff(inflights[i]->amnt, peer->channel->funding_sats); + s64 remote_splice_amnt = funding_diff - inflights[i]->splice_amnt; + + if (splice_amnt < peer->channel->view[LOCAL].lowest_splice_amnt[LOCAL]) + peer->channel->view[LOCAL].lowest_splice_amnt[LOCAL] = splice_amnt; + + if (splice_amnt < peer->channel->view[REMOTE].lowest_splice_amnt[REMOTE]) + peer->channel->view[REMOTE].lowest_splice_amnt[LOCAL] = splice_amnt; + + if (remote_splice_amnt < peer->channel->view[LOCAL].lowest_splice_amnt[REMOTE]) + peer->channel->view[LOCAL].lowest_splice_amnt[REMOTE] = remote_splice_amnt; + + if (remote_splice_amnt < peer->channel->view[REMOTE].lowest_splice_amnt[LOCAL]) + peer->channel->view[REMOTE].lowest_splice_amnt[REMOTE] = remote_splice_amnt; + } +} + +/* Called to finish an ongoing splice OR one restart from chanenl_reestablish */ +static void resume_splice_negotiation(struct peer *peer, + struct inflight *inflight, + bool skip_commitments, + enum tx_role our_role) +{ + const u8 *wit_script; + struct channel_id cid; + enum peer_wire type; + struct wally_psbt *current_psbt = inflight->psbt; + struct commitsig *their_commit; + struct witness **inws; + const struct witness **outws; + u8 der[73]; + size_t der_len; + struct bitcoin_signature splice_sig; + struct bitcoin_tx *bitcoin_tx; + u32 splice_funding_index; + const u8 *msg, *sigmsg; + u32 chan_output_index; + struct bitcoin_signature their_sig; + struct pubkey *their_pubkey; + struct bitcoin_tx *final_tx; + u8 **wit_stack; + struct tlv_txsigs_tlvs *txsig_tlvs, *their_txsigs_tlvs; + + wit_script = bitcoin_redeem_2of2(tmpctx, + &peer->channel->funding_pubkey[LOCAL], + &peer->channel->funding_pubkey[REMOTE]); + + find_channel_output(peer, current_psbt, &chan_output_index); + + splice_funding_index = find_channel_funding_input(current_psbt, + &peer->channel->funding); + + if (!skip_commitments) { + their_commit = interactive_send_commitments(peer, current_psbt, + our_role); + + inflight->last_tx = tal_steal(inflight, their_commit->tx); + inflight->last_sig = their_commit->commit_signature; + + msg = towire_channeld_update_inflight(NULL, current_psbt, + their_commit->tx, + &their_commit->commit_signature); + wire_sync_write(MASTER_FD, take(msg)); + } + + /* DTODO Validate splice tx takes none of our funds in either: + * 1) channel balance + * 2) other side sneakily adding other outputs we own + */ + + /* BOLT-a8b9f495cac28124c69cc5ee429f9ef2bacb9921 #2: + * Both nodes: + * - MUST sign the transaction using SIGHASH_ALL */ + splice_sig.sighash_type = SIGHASH_ALL; + + bitcoin_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt); + + status_info("Splice signing tx: %s", + tal_hex(tmpctx, linearize_tx(tmpctx, bitcoin_tx))); + + msg = towire_hsmd_sign_splice_tx(tmpctx, bitcoin_tx, + &peer->channel->funding_pubkey[REMOTE], + splice_funding_index); + + msg = hsm_req(tmpctx, take(msg)); + if (!fromwire_hsmd_sign_tx_reply(msg, &splice_sig)) + status_failed(STATUS_FAIL_HSM_IO, + "Reading sign_splice_tx reply: %s", + tal_hex(tmpctx, msg)); + + /* Set the splice_sig on the splice funding tx psbt */ + if (!psbt_input_set_signature(current_psbt, splice_funding_index, + &peer->channel->funding_pubkey[LOCAL], + &splice_sig)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to set signature internally " + "funding_index: %d " + "my pubkey: %s " + "my signature: %s " + "psbt: %s", + splice_funding_index, + type_to_string(tmpctx, struct pubkey, &peer->channel->funding_pubkey[LOCAL]), + type_to_string(tmpctx, struct bitcoin_signature, &splice_sig), + type_to_string(tmpctx, struct wally_psbt, current_psbt)); + + + + txsig_tlvs = tlv_txsigs_tlvs_new(tmpctx); + der_len = signature_to_der(der, &splice_sig); + txsig_tlvs->funding_outpoint_sig = tal_dup_arr(tmpctx, u8, der, + der_len, 0); + + outws = psbt_to_witnesses(tmpctx, current_psbt, + our_role, splice_funding_index); + sigmsg = towire_tx_signatures(tmpctx, &peer->channel_id, + &inflight->outpoint.txid, outws, + txsig_tlvs); + + if (do_i_sign_first(peer, current_psbt, our_role)) { + status_debug("Splice: we sign first"); + msg = towire_channeld_update_inflight(NULL, current_psbt, + NULL, NULL); + wire_sync_write(MASTER_FD, take(msg)); + peer_write(peer->pps, sigmsg); + } + + msg = peer_read(tmpctx, peer->pps); + + type = fromwire_peektype(msg); + + if (handle_peer_error(peer->pps, &peer->channel_id, msg)) + return; + + if (type != WIRE_TX_SIGNATURES) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splicing got incorrect message from peer: %s " + "(should be WIRE_TX_SIGNATURES)", + peer_wire_name(type)); + + their_txsigs_tlvs = tlv_txsigs_tlvs_new(tmpctx); + if (!fromwire_tx_signatures(tmpctx, msg, &cid, &inflight->outpoint.txid, + cast_const3(struct witness ***, &inws), + &their_txsigs_tlvs)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splicing bad tx_signatures %s", + tal_hex(msg, msg)); + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * - Upon receipt of `tx_signatures` for the splice transaction: + * - MUST consider splice negotiation complete. + * - MUST consider the connection no longer quiescent. + */ + end_stfu_mode(peer); + + /* BOLT-a8b9f495cac28124c69cc5ee429f9ef2bacb9921 #2: + * Both nodes: + * - MUST sign the transaction using SIGHASH_ALL */ + their_sig.sighash_type = SIGHASH_ALL; + + if (!signature_from_der(their_txsigs_tlvs->funding_outpoint_sig, + tal_count(their_txsigs_tlvs->funding_outpoint_sig), + &their_sig)) { + + peer_failed_warn(peer->pps, &peer->channel_id, + "Splicing bad tx_signatures %s", + tal_hex(msg, msg)); + } + + their_pubkey = &peer->channel->funding_pubkey[REMOTE]; + + /* Set the commit_sig on the commitment tx psbt */ + if (!psbt_input_set_signature(current_psbt, + splice_funding_index, + their_pubkey, + &their_sig)) { + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to set signature internally " + "funding_index: %d " + "pubkey: %s " + "signature: %s " + "psbt: %s", + splice_funding_index, + type_to_string(tmpctx, struct pubkey, their_pubkey), + type_to_string(tmpctx, struct bitcoin_signature, &their_sig), + type_to_string(tmpctx, struct wally_psbt, current_psbt)); + } + + psbt_input_set_witscript(current_psbt, + splice_funding_index, + wit_script); + + if (tal_count(inws) > current_psbt->num_inputs) + peer_failed_warn(peer->pps, &peer->channel_id, + "%lu too many witness elements received", + tal_count(inws) - current_psbt->num_inputs); + + /* We put the PSBT + sigs all together */ + for (size_t j = 0, i = 0; i < current_psbt->num_inputs; i++) { + struct wally_psbt_input *in = + ¤t_psbt->inputs[i]; + u64 in_serial; + + if (!psbt_get_serial_id(&in->unknowns, &in_serial)) { + status_broken("PSBT input %zu missing serial_id %s", + i, type_to_string(tmpctx, + struct wally_psbt, + current_psbt)); + return; + } + if (in_serial % 2 == our_role) + continue; + + if (i == splice_funding_index) + continue; + + if (j == tal_count(inws)) + peer_failed_warn(peer->pps, + &peer->channel_id, + "Mismatch witness stack count %s", + tal_hex(msg, msg)); + + psbt_finalize_input(current_psbt, in, inws[j++]); + } + + final_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt); + + wit_stack = bitcoin_witness_2of2(current_psbt, &splice_sig, &their_sig, + &peer->channel->funding_pubkey[LOCAL], + their_pubkey); + + bitcoin_tx_input_set_witness(final_tx, splice_funding_index, wit_stack); + + /* We let core validate our peer's signatures are correct. */ + + msg = towire_channeld_update_inflight(NULL, current_psbt, NULL, + NULL); + wire_sync_write(MASTER_FD, take(msg)); + + if (!do_i_sign_first(peer, current_psbt, our_role)) { + status_debug("Splice: we sign second"); + peer_write(peer->pps, sigmsg); + } + + peer->splice = tal_free(peer->splice); + + msg = towire_channeld_splice_confirmed_signed(tmpctx, final_tx, chan_output_index); + wire_sync_write(MASTER_FD, take(msg)); + + send_channel_update(peer, 0); +} + +static struct inflight *inflights_new(struct peer *peer) +{ + struct inflight *inf = tal(peer->splice_state->inflights, struct inflight); + int i = tal_count(peer->splice_state->inflights); + + if (i) + tal_resize(&peer->splice_state->inflights, i + 1); + else + peer->splice_state->inflights = tal_arr(peer->splice_state, + struct inflight *, 1); + + peer->splice_state->inflights[i] = inf; + return inf; +} + +/* ACCEPTER side of the splice. Here we handle all the accepter's steps for the + * splice. Since the channel must be in STFU mode we block the daemon here until + * the splice is finished or aborted. */ +static void splice_accepter(struct peer *peer, const u8 *inmsg) +{ + const u8 *msg; + struct interactivetx_context *ictx; + u32 splice_funding_index; + struct bitcoin_blkid genesis_blockhash; + struct channel_id channel_id; + struct amount_sat both_amount; + u32 funding_feerate_perkw; + u32 locktime; + struct pubkey splice_remote_pubkey; + char *error; + struct inflight *new_inflight; + struct wally_psbt_output *new_chan_output; + struct bitcoin_outpoint outpoint; + + /* Can't start a splice with another splice still active */ + assert(!peer->splice); + peer->splice = splice_new(peer); + + ictx = new_interactivetx_context(tmpctx, TX_ACCEPTER, + peer->pps, peer->channel_id); + + if (!fromwire_splice(inmsg, + &channel_id, + &genesis_blockhash, + &peer->splice->opener_relative, + &funding_feerate_perkw, + &locktime, + &splice_remote_pubkey)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad wire_splice %s", tal_hex(tmpctx, inmsg)); + + peer->splice_state->await_commitment_succcess = false; + + if (!is_stfu_active(peer)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Must be in STFU mode before intiating splice"); + + if (!bitcoin_blkid_eq(&genesis_blockhash, &chainparams->genesis_blockhash)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad splice blockhash"); + + if (!channel_id_eq(&channel_id, &peer->channel_id)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice internal error: mismatched channelid"); + + if (!pubkey_eq(&splice_remote_pubkey, + &peer->channel->funding_pubkey[REMOTE])) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice doesnt support changing pubkeys"); + + if (funding_feerate_perkw < peer->feerate_min) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice feerate_perkw is too low"); + + /* TODO: Add plugin hook for user to adjust accepter amount */ + peer->splice->accepter_relative = 0; + + msg = towire_splice_ack(NULL, + &peer->channel_id, + &chainparams->genesis_blockhash, + peer->splice->accepter_relative, + &peer->channel->funding_pubkey[LOCAL]); + + peer->splice->mode = true; + + peer_write(peer->pps, take(msg)); + + /* Now we wait for the other side to go first. + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The receiver of `splice_ack`: + * - MUST begin splice negotiation. + */ + + ictx->next_update_fn = next_splice_step; + ictx->desired_psbt = NULL; + ictx->pause_when_complete = false; + + error = process_interactivetx_updates(tmpctx, ictx, + &peer->splice->received_tx_complete); + if (error) + peer_failed_err(peer->pps, &peer->channel_id, + "Interactive splicing error: %s", error); + + assert(ictx->pause_when_complete == false); + peer->splice->sent_tx_complete = true; + + /* DTODO validate locktime */ + ictx->current_psbt->fallback_locktime = locktime; + + splice_funding_index = find_channel_funding_input(ictx->current_psbt, + &peer->channel->funding); + + new_chan_output = find_channel_output(peer, ictx->current_psbt, + &outpoint.n); + + both_amount = check_balances(peer, TX_ACCEPTER, ictx->current_psbt, + outpoint.n, splice_funding_index); + new_chan_output->amount = both_amount.satoshis; /* Raw: type conv */ + + psbt_elements_normalize_fees(ictx->current_psbt); + + psbt_txid(tmpctx, ictx->current_psbt, &outpoint.txid, NULL); + + psbt_finalize(ictx->current_psbt); + + status_debug("Splice accepter adding inflight: %s", psbt_to_b64(tmpctx, ictx->current_psbt)); + + msg = towire_channeld_add_inflight(NULL, + &outpoint.txid, + outpoint.n, + funding_feerate_perkw, + both_amount, + peer->splice->accepter_relative, + ictx->current_psbt, + false); + + master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_GOT_INFLIGHT); + + new_inflight = inflights_new(peer); + + psbt_txid(new_inflight, ictx->current_psbt, + &new_inflight->outpoint.txid, NULL); + new_inflight->outpoint = outpoint; + new_inflight->amnt = both_amount; + new_inflight->psbt = tal_steal(new_inflight, ictx->current_psbt); + new_inflight->splice_amnt = peer->splice->accepter_relative; + new_inflight->last_tx = NULL; + new_inflight->i_am_initiator = false; + + update_view_from_inflights(peer); + + peer->splice_state->count++; + + resume_splice_negotiation(peer, new_inflight, false, TX_ACCEPTER); +} + +static struct bitcoin_tx *bitcoin_tx_from_txid(struct peer *peer, + struct bitcoin_txid txid) +{ + u8 *msg; + struct bitcoin_tx *tx = NULL; + + msg = towire_channeld_splice_lookup_tx(NULL, &txid); + + msg = master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT); + + if (!fromwire_channeld_splice_lookup_tx_result(tmpctx, msg, &tx)) + master_badmsg(WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT, msg); + + return tx; +} + +/* splice_initiator runs when splice_ack is received by the other side. It + * handles the initial splice creation while callbacks will handle later + * stages. */ +static void splice_initiator(struct peer *peer, const u8 *inmsg) +{ + struct bitcoin_blkid genesis_blockhash; + struct channel_id channel_id; + struct pubkey splice_remote_pubkey; + size_t input_index; + const u8 *wit_script; + u8 *outmsg; + struct interactivetx_context *ictx; + struct bitcoin_tx *prev_tx; + u32 sequence = 0; + u8 *scriptPubkey; + char *error; + + status_debug("splice_initiator peer->stfu_wait_single_msg: %d", + (int)peer->stfu_wait_single_msg); + + ictx = new_interactivetx_context(tmpctx, TX_INITIATOR, + peer->pps, peer->channel_id); + + if (!fromwire_splice_ack(inmsg, + &channel_id, + &genesis_blockhash, + &peer->splice->accepter_relative, + &splice_remote_pubkey)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad wire_splice_ack %s", tal_hex(tmpctx, inmsg)); + + if (!bitcoin_blkid_eq(&genesis_blockhash, &chainparams->genesis_blockhash)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad splice[ACK] blockhash"); + + if (!channel_id_eq(&channel_id, &peer->channel_id)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice[ACK] internal error: mismatched channelid"); + + if (!pubkey_eq(&splice_remote_pubkey, &peer->channel->funding_pubkey[REMOTE])) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice[ACK] doesnt support changing pubkeys"); + + peer->splice->received_tx_complete = false; + peer->splice->sent_tx_complete = false; + peer->splice_state->locked_ready[LOCAL] = false; + peer->splice_state->locked_ready[REMOTE] = false; + + ictx->next_update_fn = next_splice_step; + ictx->pause_when_complete = true; + ictx->desired_psbt = peer->splice->current_psbt; + + /* We go first as the receiver of the ack. + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The receiver of `splice_ack`: + * - MUST begin splice negotiation. + */ + BUILD_ASSERT(NUM_SIDES == 2); + wit_script = bitcoin_redeem_2of2(tmpctx, + &peer->channel->funding_pubkey[LOCAL], + &peer->channel->funding_pubkey[REMOTE]); + + input_index = ictx->desired_psbt->num_inputs; + + /* First we spend the existing channel outpoint + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The initiator: + * - MUST `tx_add_input` an input which spends the current funding + * transaction output. + */ + psbt_append_input(ictx->desired_psbt, &peer->channel->funding, sequence, + NULL, wit_script, NULL); + + status_debug("just added funding w/ outpoint index set to %d, value in psbt input 1 is: %d, and value in psbt input 2 is: %d", + (int)peer->channel->funding.n, + (int)ictx->desired_psbt->inputs[0].index, + (int)ictx->desired_psbt->inputs[1].index); + + /* Segwit requires us to store the value of the outpoint being spent, + * so let's do that */ + scriptPubkey = scriptpubkey_p2wsh(ictx->desired_psbt, wit_script); + psbt_input_set_wit_utxo(ictx->desired_psbt, input_index, + scriptPubkey, peer->channel->funding_sats); + + /* We must loading the funding tx as our previous utxo */ + prev_tx = bitcoin_tx_from_txid(peer, peer->channel->funding.txid); + psbt_input_set_utxo(ictx->desired_psbt, input_index, prev_tx->wtx); + + /* PSBT v2 requires this */ + psbt_input_set_outpoint(ictx->desired_psbt, input_index, + peer->channel->funding); + + /* Next we add the new channel outpoint, with a 0 amount for now. It + * will be filled in later. + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The initiator: + * ... + * - MUST `tx_add_output` a zero-value output which pays to the two + * funding keys using the higher of the two `generation` fields. + */ + psbt_append_output(ictx->desired_psbt, + scriptpubkey_p2wsh(ictx->desired_psbt, wit_script), + amount_sat(0)); + + psbt_add_serials(ictx->desired_psbt, ictx->our_role); + + error = process_interactivetx_updates(tmpctx, + ictx, + &peer->splice->received_tx_complete); + + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Interactive splicing_ack error: %s", error); + + peer->splice->tx_add_input_count = ictx->tx_add_input_count; + peer->splice->tx_add_output_count = ictx->tx_add_output_count; + + if (peer->splice->current_psbt != ictx->current_psbt) + tal_free(peer->splice->current_psbt); + peer->splice->current_psbt = tal_steal(peer->splice, ictx->current_psbt); + + peer->splice->mode = true; + + /* Return the current PSBT to the channel_control to give to user. + */ + outmsg = towire_channeld_splice_confirmed_init(NULL, + ictx->current_psbt); + wire_sync_write(MASTER_FD, take(outmsg)); +} + +/* This occurs when the user has marked they are done making changes to the + * PSBT. Now we continually send `tx_complete` and intake our peer's changes + * inside `process_interactivetx_updates`. Once they are onboard indicated + * with their sending of `tx_complete` we clean up the final PSBT and return + * to the user for their final signing steps. */ +static void splice_initiator_user_finalized(struct peer *peer) +{ + u8 *outmsg; + struct interactivetx_context *ictx; + char *error; + u32 chan_output_index, splice_funding_index; + struct wally_psbt_output *new_chan_output; + struct inflight *new_inflight; + struct bitcoin_txid current_psbt_txid; + struct amount_sat both_amount; + struct commitsig *their_commit; + + ictx = new_interactivetx_context(tmpctx, TX_INITIATOR, + peer->pps, peer->channel_id); + + ictx->next_update_fn = next_splice_step; + ictx->pause_when_complete = false; + ictx->desired_psbt = ictx->current_psbt = peer->splice->current_psbt; + ictx->tx_add_input_count = peer->splice->tx_add_input_count; + ictx->tx_add_output_count = peer->splice->tx_add_output_count; + + error = process_interactivetx_updates(tmpctx, ictx, + &peer->splice->received_tx_complete); + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice finalize error: %s", error); + + /* With pause_when_complete fase, this assert should never fail */ + assert(peer->splice->received_tx_complete); + peer->splice->sent_tx_complete = true; + + psbt_sort_by_serial_id(ictx->current_psbt); + + new_chan_output = find_channel_output(peer, ictx->current_psbt, + &chan_output_index); + + splice_funding_index = find_channel_funding_input(ictx->current_psbt, + &peer->channel->funding); + + both_amount = check_balances(peer, TX_INITIATOR, ictx->current_psbt, + chan_output_index, splice_funding_index); + new_chan_output->amount = both_amount.satoshis; /* Raw: type conv */ + + psbt_elements_normalize_fees(ictx->current_psbt); + + status_debug("Splice adding inflight: %s", + psbt_to_b64(tmpctx, ictx->current_psbt)); + + psbt_txid(tmpctx, ictx->current_psbt, ¤t_psbt_txid, NULL); + + outmsg = towire_channeld_add_inflight(tmpctx, + ¤t_psbt_txid, + chan_output_index, + peer->splice->feerate_per_kw, + amount_sat(new_chan_output->amount), + peer->splice->opener_relative, + ictx->current_psbt, + true); + + master_wait_sync_reply(tmpctx, peer, take(outmsg), + WIRE_CHANNELD_GOT_INFLIGHT); + + new_inflight = inflights_new(peer); + + psbt_txid(tmpctx, ictx->current_psbt, &new_inflight->outpoint.txid, NULL); + new_inflight->outpoint.n = chan_output_index; + new_inflight->psbt = tal_steal(new_inflight, ictx->current_psbt); + new_inflight->amnt = amount_sat(new_chan_output->amount); + new_inflight->splice_amnt = peer->splice->opener_relative; + new_inflight->last_tx = NULL; + new_inflight->i_am_initiator = true; + + update_view_from_inflights(peer); + + peer->splice_state->count++; + + their_commit = interactive_send_commitments(peer, ictx->current_psbt, + TX_INITIATOR); + + new_inflight->last_tx = tal_steal(peer, their_commit->tx); + new_inflight->last_sig = their_commit->commit_signature; + + outmsg = towire_channeld_update_inflight(NULL, ictx->current_psbt, + their_commit->tx, + &their_commit->commit_signature); + wire_sync_write(MASTER_FD, take(outmsg)); + + status_debug("user_finalized peer->stfu_wait_single_msg: %d", (int)peer->stfu_wait_single_msg); + + if (peer->splice->current_psbt != ictx->current_psbt) + tal_free(peer->splice->current_psbt); + peer->splice->current_psbt = tal_steal(peer->splice, ictx->current_psbt); + outmsg = towire_channeld_splice_confirmed_update(NULL, + ictx->current_psbt, + true); + wire_sync_write(MASTER_FD, take(outmsg)); +} + +/* During a splice the user may call splice_update mulitple times adding + * new details to the active PSBT. Each user call enters here: */ +static void splice_initiator_user_update(struct peer *peer, const u8 *inmsg) +{ + u8 *outmsg, *msg; + struct interactivetx_context *ictx; + char *error; + + if (!peer->splice) { + msg = towire_channeld_splice_state_error(NULL, "Can't accept a" + " splice PSBT update" + " because this channel" + " hasn't begun a" + " splice."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + ictx = new_interactivetx_context(tmpctx, TX_INITIATOR, + peer->pps, peer->channel_id); + + if (!fromwire_channeld_splice_update(ictx, inmsg, &ictx->desired_psbt)) + master_badmsg(WIRE_CHANNELD_SPLICE_UPDATE, inmsg); + + if (!peer->splice->mode) { + msg = towire_channeld_splice_state_error(NULL, "Can't update a" + " splice when not in" + " splice mode."); + wire_sync_write(MASTER_FD, take(msg)); + return; + + } + + ictx->next_update_fn = next_splice_step; + ictx->pause_when_complete = true; + + /* Should already have a current_psbt from a previously initiated one */ + assert(peer->splice->current_psbt); + ictx->current_psbt = peer->splice->current_psbt; + ictx->tx_add_input_count = peer->splice->tx_add_input_count; + ictx->tx_add_output_count = peer->splice->tx_add_output_count; + + /* User may not have setup serial numbers on their modifeid PSBT, so we + * ensure that for them here */ + psbt_add_serials(ictx->desired_psbt, ictx->our_role); + + status_debug("splice_update start with, current psbt version: %d," + " desired: %d.", ictx->current_psbt->version, + ictx->desired_psbt->version); + + /* If there no are no changes, we consider the splice 'user finalized' */ + if (!interactivetx_has_changes(ictx, ictx->desired_psbt)) { + splice_initiator_user_finalized(peer); + return; + } + + error = process_interactivetx_updates(tmpctx, ictx, + &peer->splice->received_tx_complete); + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice update error: %s", error); + + peer->splice->tx_add_input_count = ictx->tx_add_input_count; + peer->splice->tx_add_output_count = ictx->tx_add_output_count; + + if (peer->splice->current_psbt != ictx->current_psbt) + tal_free(peer->splice->current_psbt); + peer->splice->current_psbt = tal_steal(peer->splice, ictx->current_psbt); + + /* Peer may have modified our PSBT so we return it to the user here */ + outmsg = towire_channeld_splice_confirmed_update(NULL, + ictx->current_psbt, + false); + wire_sync_write(MASTER_FD, take(outmsg)); +} + +static struct inflight *last_inflight(struct peer *peer) +{ + size_t count = tal_count(peer->splice_state->inflights); + + if (count) + return peer->splice_state->inflights[count - 1]; + + return NULL; +} + +/* This occurs when the user has signed the final version of the PSBT. At this + * point we do a commitment transaciton round with our peer via + * `interactive_send_commitments`. + * + * Then we finalize the PSBT some more and sign away our funding output, + * place that signature in the PSBT, and pass our signature to the peer and get + * theirs back. */ +static void splice_initiator_user_signed(struct peer *peer, const u8 *inmsg) +{ + struct wally_psbt *signed_psbt; + struct bitcoin_txid current_psbt_txid, signed_psbt_txid; + struct inflight *inflight; + const u8 *msg; + + if (!peer->splice) { + msg = towire_channeld_splice_state_error(NULL, "Can't accept a" + " signed splice PSBT" + " because this channel" + " hasn't begun a" + " splice."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + if (!fromwire_channeld_splice_signed(tmpctx, inmsg, &signed_psbt, + &peer->splice->force_sign_first)) + master_badmsg(WIRE_CHANNELD_SPLICE_SIGNED, inmsg); + + if (!peer->splice->mode) { + msg = towire_channeld_splice_state_error(NULL, "Can't sign a" + " splice when not in" + " splice mode."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (!peer->splice->received_tx_complete) { + msg = towire_channeld_splice_state_error(NULL, "Can't sign a" + " splice when we" + " haven't received" + " tx_complete yet."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (!peer->splice->sent_tx_complete) { + msg = towire_channeld_splice_state_error(NULL, "Can't sign a" + " splice when we" + " haven't sent" + " tx_complete yet."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + psbt_txid(tmpctx, peer->splice->current_psbt, ¤t_psbt_txid, NULL); + psbt_txid(tmpctx, signed_psbt, &signed_psbt_txid, NULL); + + if (!bitcoin_txid_eq(&signed_psbt_txid, ¤t_psbt_txid)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Signed PSBT txid %s does not match" + " current_psbt_txid %s", + type_to_string(tmpctx, struct bitcoin_txid, + &signed_psbt_txid), + type_to_string(tmpctx, struct bitcoin_txid, + ¤t_psbt_txid)); + + peer->splice->current_psbt = tal_free(peer->splice->current_psbt); + + inflight = last_inflight(peer); + inflight->psbt = tal_steal(inflight, signed_psbt); + + resume_splice_negotiation(peer, inflight, true, TX_INITIATOR); +} + +/* This occurs once our 'stfu' transition was successful. */ +static void handle_splice_stfu_success(struct peer *peer) +{ + u8 *msg = towire_splice(tmpctx, + &peer->channel_id, + &chainparams->genesis_blockhash, + peer->splice->opener_relative, + peer->splice->feerate_per_kw, + peer->splice->current_psbt->fallback_locktime, + &peer->channel->funding_pubkey[LOCAL]); + peer->splice_state->await_commitment_succcess = false; + peer_write(peer->pps, take(msg)); +} + +/* User has begun a splice with `splice_init` command. Here we request entry + * into STFU mode, when we get it, send `splice` to our peer-> + * Later the peer will send `splice_ack` and the code that starts the actual + * splice happens at that point in `splice_initiator()`. */ +static void handle_splice_init(struct peer *peer, const u8 *inmsg) +{ + u8 *msg; + + /* Can't start a splice with another splice still active */ + if (peer->splice) { + msg = towire_channeld_splice_state_error(NULL, "Can't start two" + " splices on the same" + " channel at once."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + peer->splice = splice_new(peer); + + if (!fromwire_channeld_splice_init(peer, inmsg, &peer->splice->current_psbt, + &peer->splice->opener_relative, + &peer->splice->feerate_per_kw, + &peer->splice->force_feerate)) + master_badmsg(WIRE_CHANNELD_SPLICE_INIT, inmsg); + + if (peer->want_stfu) { + msg = towire_channeld_splice_state_error(NULL, "Can't begin a" + " splice while waiting" + " for STFU."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (is_stfu_active(peer)) { + msg = towire_channeld_splice_state_error(NULL, "Can't begin a" + " splice while" + " currently in STFU"); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (peer->splice->mode) { + msg = towire_channeld_splice_state_error(NULL, "Can't begin a" + " splice while already" + " doing a splice."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (peer->splice->feerate_per_kw < peer->feerate_min) { + msg = towire_channeld_splice_state_error(NULL, tal_fmt(tmpctx, + "Feerate %u is too" + " low. Lower than" + " channel feerate_min" + " %u", + peer->splice->feerate_per_kw, + peer->feerate_min)); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + status_debug("Getting handle_splice_init psbt version %d", peer->splice->current_psbt->version); + + peer->on_stfu_success = handle_splice_stfu_success; + + /* First things first we must STFU the channel */ + peer->stfu_initiator = LOCAL; + peer->want_stfu = true; + maybe_send_stfu(peer); +} + +static void peer_in(struct peer *peer, const u8 *msg) +{ + enum peer_wire type = fromwire_peektype(msg); + + if (handle_peer_error(peer->pps, &peer->channel_id, msg)) + return; + + /* Must get channel_ready before almost anything. */ + if (!peer->channel_ready[REMOTE]) { + if (type != WIRE_CHANNEL_READY + && type != WIRE_SHUTDOWN + /* We expect these for v2 !! */ + && type != WIRE_TX_SIGNATURES + /* lnd sends these early; it's harmless. */ + && type != WIRE_UPDATE_FEE + && type != WIRE_ANNOUNCEMENT_SIGNATURES) { + peer_failed_warn(peer->pps, &peer->channel_id, + "%s (%u) before funding locked", + peer_wire_name(type), type); + } + } + + /* For cleaner errors, we check message is valid during STFU mode */ + if (peer->stfu_wait_single_msg) + if (!VALID_STFU_MESSAGE(type)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Got invalid message during STFU " + "mode: %s", + peer_wire_name(type)); + + peer->stfu_wait_single_msg = false; + + switch (type) { + case WIRE_CHANNEL_READY: + handle_peer_channel_ready(peer, msg); + return; + case WIRE_ANNOUNCEMENT_SIGNATURES: + handle_peer_announcement_signatures(peer, msg); + return; + case WIRE_UPDATE_ADD_HTLC: + handle_peer_add_htlc(peer, msg); + return; + case WIRE_COMMITMENT_SIGNED: + handle_peer_commit_sig(peer, msg, 0, NULL, 0, 0); + return; + case WIRE_UPDATE_FEE: + handle_peer_feechange(peer, msg); + return; + case WIRE_UPDATE_BLOCKHEIGHT: + handle_peer_blockheight_change(peer, msg); + return; + case WIRE_REVOKE_AND_ACK: + handle_peer_revoke_and_ack(peer, msg); + return; + case WIRE_UPDATE_FULFILL_HTLC: + handle_peer_fulfill_htlc(peer, msg); + return; + case WIRE_UPDATE_FAIL_HTLC: + handle_peer_fail_htlc(peer, msg); + return; + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + handle_peer_fail_malformed_htlc(peer, msg); + return; + case WIRE_SHUTDOWN: + handle_peer_shutdown(peer, msg); + return; + case WIRE_STFU: handle_stfu(peer, msg); return; + case WIRE_SPLICE: + splice_accepter(peer, msg); + return; + case WIRE_SPLICE_ACK: + splice_initiator(peer, msg); + return; + case WIRE_SPLICE_LOCKED: + handle_peer_splice_locked(peer, msg); + return; case WIRE_INIT: case WIRE_OPEN_CHANNEL: case WIRE_ACCEPT_CHANNEL: @@ -2486,9 +4338,11 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) htlc_sigs = calc_commitsigs(tmpctx, peer, txs, funding_wscript, htlc_map, peer->next_index[REMOTE]-1, &commit_sig); + msg = towire_commitment_signed(NULL, &peer->channel_id, &commit_sig.s, - raw_sigs(tmpctx, htlc_sigs)); + raw_sigs(tmpctx, htlc_sigs), + NULL); peer_write(peer->pps, take(msg)); /* If we have already received the revocation for the previous, the @@ -2731,6 +4585,10 @@ static void peer_reconnect(struct peer *peer, struct secret last_local_per_commitment_secret; bool dataloss_protect, check_extra_fields; const u8 **premature_msgs = tal_arr(peer, const u8 *, 0); + struct inflight *inflight; + bool next_matches_current, next_matches_inflight; + struct bitcoin_txid *local_next_funding, *remote_next_funding; + struct tlv_channel_reestablish_tlvs *send_tlvs, *recv_tlvs; dataloss_protect = feature_negotiated(peer->our_features, @@ -2750,6 +4608,12 @@ static void peer_reconnect(struct peer *peer, /* Subtle: we free tmpctx below as we loop, so tal off peer */ send_tlvs = tlv_channel_reestablish_tlvs_new(peer); + inflight = last_inflight(peer); + + /* If inflight with no sigs on it, send next_funding */ + if (inflight && !inflight->last_tx) + send_tlvs->next_funding = &inflight->outpoint.txid; + /* BOLT-upgrade_protocol #2: * A node sending `channel_reestablish`, if it supports upgrading channels: * - MUST set `next_to_send` the commitment number of the next @@ -3129,7 +4993,6 @@ static void peer_reconnect(struct peer *peer, if (type) set_channel_type(peer->channel, type); } - tal_free(send_tlvs); /* Now stop, we've been polite long enough. */ if (reestablish_only) { @@ -3144,6 +5007,57 @@ static void peer_reconnect(struct peer *peer, "Channel is already closed"); } + local_next_funding = (send_tlvs ? send_tlvs->next_funding : NULL); + remote_next_funding = (recv_tlvs ? recv_tlvs->next_funding : NULL); + + if (local_next_funding || remote_next_funding) { + if (remote_next_funding) { + next_matches_current = bitcoin_txid_eq(remote_next_funding, + &peer->channel->funding.txid); + if (inflight) + next_matches_inflight = bitcoin_txid_eq(remote_next_funding, + &inflight->outpoint.txid); + } + if (remote_next_funding && !next_matches_current + && !next_matches_inflight) { + peer_failed_err(peer->pps, + &peer->channel_id, + "Unrecognized next_funding txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + remote_next_funding)); + } else if (inflight && !next_matches_inflight) { + /* DTODO: tx_abort */ + peer_failed_warn(peer->pps, &peer->channel_id, + "next_funding txid %s doesnt match" + " our inflight txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + &inflight->outpoint.txid), + type_to_string(tmpctx, + struct bitcoin_txid, + &peer->channel->funding.txid)); + } else if (!inflight && !next_matches_current) { + /* DTODO: tx_abort */ + peer_failed_warn(peer->pps, &peer->channel_id, + "next_funding txid %s doesnt match" + " our confirmed funding txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + remote_next_funding), + type_to_string(tmpctx, + struct bitcoin_txid, + &peer->channel->funding.txid)); + } + else { + resume_splice_negotiation(peer, inflight, false, + inflight->i_am_initiator + ? TX_INITIATOR + : TX_ACCEPTER); + } + } + tal_free(send_tlvs); + /* 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. */ @@ -3184,12 +5098,16 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) struct short_channel_id *scid, *alias_local; struct tlv_channel_ready_tlvs *tlvs; struct pubkey point; + bool splicing; + struct bitcoin_txid txid; if (!fromwire_channeld_funding_depth(tmpctx, msg, &scid, &alias_local, - &depth)) + &depth, + &splicing, + &txid)) master_badmsg(WIRE_CHANNELD_FUNDING_DEPTH, msg); /* Too late, we're shutting down! */ @@ -3198,18 +5116,32 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) if (depth < peer->channel->minimum_depth) { peer->depth_togo = peer->channel->minimum_depth - depth; - - } else { + } + else { peer->depth_togo = 0; - /* 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; + /* For splicing we only update the short channel id on mutual + * splice lock */ + if (splicing) { + peer->splice_state->short_channel_id = *scid; + status_debug("Current channel id is %s, " + "splice_short_channel_id now set to %s", + type_to_string(tmpctx, + struct short_channel_id, + &peer->short_channel_ids[LOCAL]), + type_to_string(tmpctx, + struct short_channel_id, + &peer->splice_state->short_channel_id)); + } else { + /* If we know an actual short_channel_id prefer to use + * that, otherwise fill in the alias. From channeld's + * point of view switching from zeroconf to an actual + * funding scid is just a reorg. */ + if (scid) + peer->short_channel_ids[LOCAL] = *scid; + else if (alias_local) + peer->short_channel_ids[LOCAL] = *alias_local; + } if (!peer->channel_ready[LOCAL]) { status_debug("channel_ready: sending commit index" @@ -3231,6 +5163,18 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) peer->channel_ready[LOCAL] = true; } + else if(splicing && !peer->splice_state->locked_ready[LOCAL]) { + assert(scid); + + msg = towire_splice_locked(NULL, &peer->channel_id); + + peer->splice_state->locked_txid = txid; + + peer_write(peer->pps, take(msg)); + + peer->splice_state->locked_ready[LOCAL] = true; + check_mutual_splice_locked(peer); + } peer->announce_depth_reached = (depth >= ANNOUNCE_MIN_DEPTH); @@ -3610,10 +5554,10 @@ static void handle_dev_quiesce(struct peer *peer, const u8 *msg) master_badmsg(WIRE_CHANNELD_DEV_QUIESCE, msg); /* Don't do this twice. */ - if (peer->stfu) + if (peer->want_stfu) status_failed(STATUS_FAIL_MASTER_IO, "dev_quiesce already"); - peer->stfu = true; + peer->want_stfu = true; peer->stfu_initiator = LOCAL; maybe_send_stfu(peer); } @@ -3666,6 +5610,23 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_CHANNEL_UPDATE: handle_channel_update(peer, msg); return; + case WIRE_CHANNELD_SPLICE_INIT: + handle_splice_init(peer, msg); + return; + case WIRE_CHANNELD_SPLICE_UPDATE: + splice_initiator_user_update(peer, msg); + return; + case WIRE_CHANNELD_SPLICE_SIGNED: + splice_initiator_user_signed(peer, msg); + return; + case WIRE_CHANNELD_SPLICE_CONFIRMED_INIT: + case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED: + case WIRE_CHANNELD_SPLICE_CONFIRMED_UPDATE: + case WIRE_CHANNELD_SPLICE_LOOKUP_TX: + case WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT: + case WIRE_CHANNELD_SPLICE_FEERATE_ERROR: + case WIRE_CHANNELD_SPLICE_FUNDING_ERROR: + break; #if DEVELOPER case WIRE_CHANNELD_DEV_REENABLE_COMMIT: handle_dev_reenable_commit(peer); @@ -3690,6 +5651,7 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_GOT_COMMITSIG_REPLY: case WIRE_CHANNELD_GOT_REVOKE_REPLY: case WIRE_CHANNELD_GOT_CHANNEL_READY: + case WIRE_CHANNELD_GOT_SPLICE_LOCKED: case WIRE_CHANNELD_GOT_ANNOUNCEMENT: case WIRE_CHANNELD_GOT_SHUTDOWN: case WIRE_CHANNELD_SHUTDOWN_COMPLETE: @@ -3703,6 +5665,10 @@ static void req_in(struct peer *peer, const u8 *msg) 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: + case WIRE_CHANNELD_SPLICE_STATE_ERROR: break; } master_badmsg(-1, msg); @@ -3801,12 +5767,16 @@ static void init_channel(struct peer *peer) &pbases, &reestablish_only, &peer->channel_update, - &peer->experimental_upgrade)) { + &peer->experimental_upgrade, + &peer->splice_state->inflights)) { master_badmsg(WIRE_CHANNELD_INIT, msg); } peer->final_index = tal_dup(peer, u32, &final_index); peer->final_ext_key = tal_dup(peer, struct ext_key, &final_ext_key); + peer->splice_state->committed_count = tal_count(peer->splice_state->inflights); + peer->splice_state->revoked_count = tal_count(peer->splice_state->inflights); + peer->splice_state->count = tal_count(peer->splice_state->inflights); #if DEVELOPER peer->dev_disable_commit = dev_disable_commit; @@ -3897,6 +5867,8 @@ static void init_channel(struct peer *peer) /* We don't need these any more, so free them. */ tal_free(htlcs); + update_view_from_inflights(peer); + peer->channel_direction = node_id_idx(&peer->node_ids[LOCAL], &peer->node_ids[REMOTE]); @@ -3948,9 +5920,14 @@ int main(int argc, char *argv[]) peer->shutdown_wrong_funding = NULL; peer->last_update_timestamp = 0; peer->last_empty_commitment = 0; - peer->stfu = false; + 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; + peer->on_stfu_success = NULL; peer->update_queue = msg_queue_new(peer, false); + peer->splice_state = splice_state_new(peer); + peer->splice = NULL; /* We send these to HSM to get real signatures; don't have valgrind * complain. */ @@ -4012,6 +5989,11 @@ int main(int argc, char *argv[]) tptr = &timeout; } + /* If we're in STFU mode and aren't waiting for a STFU mode + * specific message, don't read from the peer. */ + if (is_stfu_active(peer) && !peer->stfu_wait_single_msg) + FD_CLR(peer->pps->peer_fd, &rfds); + if (select(nfds, &rfds, NULL, NULL, tptr) < 0) { /* Signals OK, eg. SIGUSR1 */ if (errno == EINTR) diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 03af74bf7cc2..5174c18e0f95 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -83,6 +84,8 @@ msgdata,channeld_init,reestablish_only,bool, msgdata,channeld_init,channel_update_len,u16, msgdata,channeld_init,channel_update,u8,channel_update_len msgdata,channeld_init,experimental_upgrade,bool, +msgdata,channeld_init,num_inflights,u16, +msgdata,channeld_init,inflights,inflight,num_inflights # master->channeld funding hit new depth(funding locked if >= lock depth) # alias != NULL if zeroconf and short_channel_id == NULL @@ -91,6 +94,8 @@ 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, # Tell channel to offer this htlc msgtype,channeld_offer_htlc,1004 @@ -122,6 +127,12 @@ msgtype,channeld_got_channel_ready,1019 msgdata,channeld_got_channel_ready,next_per_commit_point,pubkey, msgdata,channeld_got_channel_ready,alias,?short_channel_id, +# When we receive funding_locked. +msgtype,channeld_got_splice_locked,1119 +msgdata,channeld_got_splice_locked,funding_sats,amount_sat, +msgdata,channeld_got_splice_locked,splice_amnt,s64, +msgdata,channeld_got_splice_locked,locked_txid,bitcoin_txid, + #include # When we send a commitment_signed message, tell master. @@ -160,6 +171,14 @@ msgdata,channeld_got_commitsig,failed,failed_htlc,num_failed msgdata,channeld_got_commitsig,num_changed,u16, msgdata,channeld_got_commitsig,changed,changed_htlc,num_changed msgdata,channeld_got_commitsig,tx,bitcoin_tx, +# Inflight splice commitments +msgdata,channeld_got_commitsig,num_inflight_commitsigs,u16, +msgdata,channeld_got_commitsig,inflight_commitsigs,commitsig,num_inflight_commitsigs +subtype,commitsig +subtypedata,commitsig,tx,bitcoin_tx, +subtypedata,commitsig,commit_signature,bitcoin_signature, +subtypedata,commitsig,num_htlcs,u16, +subtypedata,commitsig,htlc_signatures,bitcoin_signature,num_htlcs # Wait for reply, to make sure it's on disk before we send revocation. msgtype,channeld_got_commitsig_reply,1121 @@ -182,6 +201,79 @@ msgdata,channeld_got_revoke,penalty_tx,?bitcoin_tx, msgtype,channeld_got_revoke_reply,1122 #include + +# master->channeld: hello, I'd like to start a channel splice open +msgtype,channeld_splice_init,7204 +msgdata,channeld_splice_init,psbt,wally_psbt, +msgdata,channeld_splice_init,relative_amount,s64, +msgdata,channeld_splice_init,feerate_per_kw,u32, +msgdata,channeld_splice_init,force_feerate,bool, + +# channeld->master: hello, I started a channel splice open +msgtype,channeld_splice_confirmed_init,7205 +msgdata,channeld_splice_confirmed_init,psbt,wally_psbt, + +# master->channeld: Update an active splice +msgtype,channeld_splice_update,7206 +msgdata,channeld_splice_update,psbt,wally_psbt, + +# channeld->master: Splice update complete +msgtype,channeld_splice_confirmed_update,7207 +msgdata,channeld_splice_confirmed_update,psbt,wally_psbt, +msgdata,channeld_splice_confirmed_update,commitments_secured,bool, + +# channeld->master: Lookup a transaction +msgtype,channeld_splice_lookup_tx,7208 +msgdata,channeld_splice_lookup_tx,txid,bitcoin_txid, + +# master->channeld: Retrieved transaction +msgtype,channeld_splice_lookup_tx_result,7209 +msgdata,channeld_splice_lookup_tx_result,tx,bitcoin_tx, + +# master->channeld: User has signed psbt and it's ready to complete +msgtype,channeld_splice_signed,7212 +msgdata,channeld_splice_signed,psbt,wally_psbt, +msgdata,channeld_splice_signed,force_sign_first,bool, + +# channeld->master: Signed psbt is completed +msgtype,channeld_splice_confirmed_signed,7213 +msgdata,channeld_splice_confirmed_signed,tx,bitcoin_tx, +msgdata,channeld_splice_confirmed_signed,output_index,u32, + +# channeld->master: A feerate error has occured +msgtype,channeld_splice_feerate_error,7215 +msgdata,channeld_splice_feerate_error,fee,amount_msat, +msgdata,channeld_splice_feerate_error,too_high,bool, + +# channeld->master: Add an inflight to the DB +msgtype,channeld_add_inflight,7216 +msgdata,channeld_add_inflight,tx_id,bitcoin_txid, +msgdata,channeld_add_inflight,tx_outnum,u32, +msgdata,channeld_add_inflight,feerate,u32, +msgdata,channeld_add_inflight,satoshis,amount_sat, +msgdata,channeld_add_inflight,splice_amount,s64, +msgdata,channeld_add_inflight,psbt,wally_psbt, +msgdata,channeld_add_inflight,i_am_initiator,bool, + +# master->channeld: Inflight saved successfully +msgtype,channeld_got_inflight,7217 + +# channeld->master: Update inflight with sigs +msgtype,channeld_update_inflight,7219 +msgdata,channeld_update_inflight,psbt,wally_psbt, +msgdata,channeld_update_inflight,last_tx,?bitcoin_tx, +msgdata,channeld_update_inflight,last_sig,?bitcoin_signature, + +# channeld->master: A funding error has occured +msgtype,channeld_splice_funding_error,7220 +msgdata,channeld_splice_funding_error,funding,amount_msat, +msgdata,channeld_splice_funding_error,req_funding,amount_msat, +msgdata,channeld_splice_funding_error,opener_error,bool, + +# channeld->master: A splice state error has occured +msgtype,channeld_splice_state_error,7221 +msgdata,channeld_splice_state_error,state_error,wirestring, + # Tell peer to shut down channel. msgtype,channeld_send_shutdown,1023 msgdata,channeld_send_shutdown,final_index,?u32, diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 564df20385b0..888cf86f9f2c 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -308,10 +308,30 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, const struct pubkey *per_commitment_point, u64 commitment_number, enum side side) +{ + return channel_splice_txs(ctx, &channel->funding, channel->funding_sats, + htlcmap, direct_outputs, funding_wscript, + channel, per_commitment_point, + commitment_number, side, 0, 0); +} + +struct bitcoin_tx **channel_splice_txs(const tal_t *ctx, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + const struct htlc ***htlcmap, + struct wally_tx_output *direct_outputs[NUM_SIDES], + const u8 **funding_wscript, + const struct channel *channel, + const struct pubkey *per_commitment_point, + u64 commitment_number, + enum side side, + s64 splice_amnt, + s64 remote_splice_amnt) { struct bitcoin_tx **txs; const struct htlc **committed; struct keyset keyset; + struct amount_msat side_pay, other_side_pay; if (!derive_keyset(per_commitment_point, &channel->basepoints[side], @@ -329,10 +349,25 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, &channel->funding_pubkey[side], &channel->funding_pubkey[!side]); + side_pay = channel->view[side].owed[side]; + other_side_pay = channel->view[side].owed[!side]; + + if (side == LOCAL) { + if (!amount_msat_add_sat_s64(&side_pay, side_pay, splice_amnt)) + return NULL; + if (!amount_msat_add_sat_s64(&other_side_pay, other_side_pay, remote_splice_amnt)) + return NULL; + } else if (side == REMOTE) { + if (!amount_msat_add_sat_s64(&side_pay, side_pay, remote_splice_amnt)) + return NULL; + if (!amount_msat_add_sat_s64(&other_side_pay, other_side_pay, splice_amnt)) + return NULL; + } + txs = tal_arr(ctx, struct bitcoin_tx *, 1); txs[0] = commit_tx( - ctx, &channel->funding, - channel->funding_sats, + txs, funding, + funding_sats, &channel->funding_pubkey[side], &channel->funding_pubkey[!side], channel->opener, @@ -340,8 +375,8 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, channel->lease_expiry, channel_blockheight(channel, side), &keyset, channel_feerate(channel, side), - channel->config[side].dust_limit, channel->view[side].owed[side], - channel->view[side].owed[!side], committed, htlcmap, direct_outputs, + channel->config[side].dust_limit, side_pay, + other_side_pay, committed, htlcmap, direct_outputs, commitment_number ^ channel->commitment_number_obscurer, channel_has(channel, OPT_ANCHOR_OUTPUTS), channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX), @@ -371,8 +406,22 @@ static bool get_room_above_reserve(const struct channel *channel, /* Reserve is set by the *other* side */ struct amount_sat reserve = channel->config[!side].channel_reserve; struct balance balance; + struct amount_msat owed = view->owed[side]; + + /* `lowest_splice_amnt` will always be negative or 0 */ + if (amount_msat_less_eq_sat(owed, amount_sat(-view->lowest_splice_amnt[side]))) { + status_debug("Relative splice balance invalid"); + return false; + } + + /* `lowest_splice_amnt` is a relative amount */ + if (!amount_msat_sub_sat(&owed, owed, + amount_sat(-view->lowest_splice_amnt[side]))) { + status_debug("Owed amount should not wrap around from splice"); + return false; + } - to_balance(&balance, view->owed[side]); + to_balance(&balance, owed); for (size_t i = 0; i < tal_count(removing); i++) balance_remove_htlc(&balance, removing[i], side); diff --git a/channeld/full_channel.h b/channeld/full_channel.h index 6f674a75f2c0..c0fbe6b7d4c8 100644 --- a/channeld/full_channel.h +++ b/channeld/full_channel.h @@ -76,6 +76,22 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, u64 commitment_number, enum side side); +/* Version of `channel_txs` that lets you specify a custom funding outpoint + * and funding_sats. + */ +struct bitcoin_tx **channel_splice_txs(const tal_t *ctx, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + const struct htlc ***htlcmap, + struct wally_tx_output *direct_outputs[NUM_SIDES], + const u8 **funding_wscript, + const struct channel *channel, + const struct pubkey *per_commitment_point, + u64 commitment_number, + enum side side, + s64 splice_amnt, + s64 remote_splice_amnt); + /** * actual_feerate: what is the actual feerate for the local side. * @channel: The channel state diff --git a/channeld/inflight.c b/channeld/inflight.c new file mode 100644 index 000000000000..77f0fa996381 --- /dev/null +++ b/channeld/inflight.c @@ -0,0 +1,42 @@ +#include "config.h" +#include +#include +#include +#include + +struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max) +{ + struct inflight *inflight = tal(ctx, struct inflight); + + fromwire_bitcoin_outpoint(cursor, max, &inflight->outpoint); + inflight->amnt = fromwire_amount_sat(cursor, max); + inflight->psbt = fromwire_wally_psbt(inflight, cursor, max); + inflight->splice_amnt = fromwire_s64(cursor, max); + inflight->last_tx = fromwire_bitcoin_tx(inflight, cursor, max); + fromwire_bitcoin_signature(cursor, max, &inflight->last_sig); + inflight->i_am_initiator = fromwire_bool(cursor, max); + + return inflight; +} + +void towire_inflight(u8 **pptr, const struct inflight *inflight) +{ + towire_bitcoin_outpoint(pptr, &inflight->outpoint); + towire_amount_sat(pptr, inflight->amnt); + towire_wally_psbt(pptr, inflight->psbt); + towire_s64(pptr, inflight->splice_amnt); + towire_bitcoin_tx(pptr, inflight->last_tx); + towire_bitcoin_signature(pptr, &inflight->last_sig); + towire_bool(pptr, inflight->i_am_initiator); +} + +void copy_inflight(struct inflight *dest, struct inflight *src) +{ + dest->outpoint = src->outpoint; + dest->amnt = src->amnt; + dest->psbt = src->psbt ? clone_psbt(dest, src->psbt): NULL; + dest->splice_amnt = src->splice_amnt; + dest->last_tx = src->last_tx ? clone_bitcoin_tx(dest, src->last_tx) : NULL; + dest->last_sig = src->last_sig; + dest->i_am_initiator = src->i_am_initiator; +} diff --git a/channeld/inflight.h b/channeld/inflight.h new file mode 100644 index 000000000000..c95acf9af215 --- /dev/null +++ b/channeld/inflight.h @@ -0,0 +1,24 @@ +#ifndef LIGHTNING_CHANNELD_INFLIGHT_H +#define LIGHTNING_CHANNELD_INFLIGHT_H + +#include "config.h" +#include +#include + +struct inflight { + struct bitcoin_outpoint outpoint; + struct amount_sat amnt; + struct wally_psbt *psbt; + s64 splice_amnt; + struct bitcoin_tx *last_tx; + /* last_sig is assumed valid if last_tx is set */ + struct bitcoin_signature last_sig; + bool i_am_initiator; +}; + +struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max); +void towire_inflight(u8 **pptr, const struct inflight *inflight); + +void copy_inflight(struct inflight *dest, struct inflight *src); + +#endif /* LIGHTNING_CHANNELD_INFLIGHT_H */ diff --git a/channeld/splice.c b/channeld/splice.c new file mode 100644 index 000000000000..e3019f97737d --- /dev/null +++ b/channeld/splice.c @@ -0,0 +1,37 @@ +#include "config.h" +#include +#include + +struct splice_state *splice_state_new(const tal_t *ctx) +{ + struct splice_state *splice_state = tal(ctx, struct splice_state); + + splice_state->committed_count = 0; + splice_state->revoked_count = 0; + splice_state->count = 0; + splice_state->locked_ready[LOCAL] = false; + splice_state->locked_ready[REMOTE] = false; + splice_state->await_commitment_succcess = false; + splice_state->inflights = NULL; + + return splice_state; +} + +struct splice *splice_new(const tal_t *ctx) +{ + struct splice *splice = tal(ctx, struct splice); + + splice->opener_relative = 0; + splice->accepter_relative = 0; + splice->feerate_per_kw = 0; + splice->force_feerate = false; + splice->force_sign_first = false; + splice->mode = false; + splice->tx_add_input_count = 0; + splice->tx_add_output_count = 0; + splice->current_psbt = NULL; + splice->received_tx_complete = false; + splice->sent_tx_complete = false; + + return splice; +} diff --git a/channeld/splice.h b/channeld/splice.h new file mode 100644 index 000000000000..8a9fc6552870 --- /dev/null +++ b/channeld/splice.h @@ -0,0 +1,63 @@ +#ifndef LIGHTNING_CHANNELD_SPLICE_H +#define LIGHTNING_CHANNELD_SPLICE_H + +#include "config.h" +#include +#include +#include +#include + +/* The channel's general splice state for tracking splice candidates */ +struct splice_state { + /* The active inflights */ + struct inflight **inflights; + /* The pending short channel id for a splice. Set when mutual lock. */ + struct short_channel_id short_channel_id; + /* Set to old short channel id when mutual lock occurs. */ + struct short_channel_id last_short_channel_id; + /* Tally of which sides are locked, or not */ + bool locked_ready[NUM_SIDES]; + /* Set to true when commitment cycle completes successfully */ + bool await_commitment_succcess; + /* The txid of which splice inflight was confirmed */ + struct bitcoin_txid locked_txid; + /* The number of splices that have been signed & committed */ + u32 committed_count; + /* the number of splices that have been revoke_and_ack'ed */ + u32 revoked_count; + /* The number of splices that are active (awaiting confirmation) */ + u32 count; +}; + +/* Sets `splice_state` items to default values */ +struct splice_state *splice_state_new(const tal_t *ctx); + +/* An active splice negotiation. Born when splice beings and dies when a splice + * negotation has finished */ +struct splice { + /* The opener side's relative balance change */ + s64 opener_relative; + /* The accepter side's relative balance change */ + s64 accepter_relative; + /* The feerate for the splice (on set for the initiator) */ + u32 feerate_per_kw; + /* If the feerate is higher than max, don't abort the splice */ + bool force_feerate; + /* Make our side sign first */ + bool force_sign_first; + /* After `splice` and `splice_ack` occur, we are in splice mode */ + bool mode; + /* Track how many of each tx collab msg we receive */ + u16 tx_add_input_count, tx_add_output_count; + /* Current negoitated psbt */ + struct wally_psbt *current_psbt; + /* If, in the last splice_update, was tx_complete was received */ + bool received_tx_complete; + /* If, in the last splice_update, we sent tx_complete */ + bool sent_tx_complete; +}; + +/* Sets `splice` items to default values */ +struct splice *splice_new(const tal_t *ctx); + +#endif /* LIGHTNING_CHANNELD_SPLICE_H */ diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index e9f789d5368e..e16dde3c3528 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -243,6 +243,7 @@ message ListpeersPeersChannelsInflight { string feerate = 3; Amount total_funding_msat = 4; Amount our_funding_msat = 5; + optional sint64 splice_amount = 7; bytes scratch_txid = 6; } @@ -1122,6 +1123,7 @@ message ListpeerchannelsChannels { ONCHAIN = 8; DUALOPEND_OPEN_INIT = 9; DUALOPEND_AWAITING_LOCKIN = 10; + CHANNELD_AWAITING_SPLICE = 11; } optional bytes peer_id = 1; optional bool peer_connected = 2; @@ -1186,6 +1188,7 @@ message ListpeerchannelsChannelsInflight { optional uint32 funding_outnum = 2; optional string feerate = 3; optional Amount total_funding_msat = 4; + optional sint64 splice_amount = 7; optional Amount our_funding_msat = 5; optional bytes scratch_txid = 6; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 9c384db73fe0..bb36da54f32d 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -108,6 +108,7 @@ impl From for pb::ListpeersPeersChann feerate: c.feerate, // Rule #2 for type string total_funding_msat: Some(c.total_funding_msat.into()), // Rule #2 for type msat our_funding_msat: Some(c.our_funding_msat.into()), // Rule #2 for type msat + splice_amount: c.splice_amount, // Rule #2 for type integer? scratch_txid: hex::decode(&c.scratch_txid).unwrap(), // Rule #2 for type txid } } @@ -960,6 +961,7 @@ impl From for pb::ListpeerchannelsC funding_outnum: c.funding_outnum, // Rule #2 for type u32? feerate: c.feerate, // Rule #2 for type string? total_funding_msat: c.total_funding_msat.map(|f| f.into()), // Rule #2 for type msat? + splice_amount: c.splice_amount, // Rule #2 for type integer? our_funding_msat: c.our_funding_msat.map(|f| f.into()), // Rule #2 for type msat? scratch_txid: c.scratch_txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid? } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 1fbd88286db6..58096b20741f 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1866,6 +1866,8 @@ pub mod responses { pub feerate: String, pub total_funding_msat: Amount, pub our_funding_msat: Amount, + #[serde(skip_serializing_if = "Option::is_none")] + pub splice_amount: Option, pub scratch_txid: String, } @@ -3629,6 +3631,8 @@ pub mod responses { DUALOPEND_OPEN_INIT, #[serde(rename = "DUALOPEND_AWAITING_LOCKIN")] DUALOPEND_AWAITING_LOCKIN, + #[serde(rename = "CHANNELD_AWAITING_SPLICE")] + CHANNELD_AWAITING_SPLICE, } impl TryFrom for ListpeerchannelsChannelsState { @@ -3646,6 +3650,7 @@ pub mod responses { 8 => Ok(ListpeerchannelsChannelsState::ONCHAIN), 9 => Ok(ListpeerchannelsChannelsState::DUALOPEND_OPEN_INIT), 10 => Ok(ListpeerchannelsChannelsState::DUALOPEND_AWAITING_LOCKIN), + 11 => Ok(ListpeerchannelsChannelsState::CHANNELD_AWAITING_SPLICE), o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeerchannelsChannelsState", o)), } } @@ -3665,6 +3670,7 @@ pub mod responses { ListpeerchannelsChannelsState::ONCHAIN => "ONCHAIN", ListpeerchannelsChannelsState::DUALOPEND_OPEN_INIT => "DUALOPEND_OPEN_INIT", ListpeerchannelsChannelsState::DUALOPEND_AWAITING_LOCKIN => "DUALOPEND_AWAITING_LOCKIN", + ListpeerchannelsChannelsState::CHANNELD_AWAITING_SPLICE => "CHANNELD_AWAITING_SPLICE", }.to_string() } } @@ -3688,6 +3694,8 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub total_funding_msat: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub splice_amount: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub our_funding_msat: Option, #[serde(skip_serializing_if = "Option::is_none")] pub scratch_txid: Option, diff --git a/common/amount.c b/common/amount.c index ca48d86e0af9..975ac4414ca6 100644 --- a/common/amount.c +++ b/common/amount.c @@ -37,6 +37,14 @@ struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat) return sat; } +struct amount_msat amount_msat_to_sat_remainder(struct amount_msat msat) +{ + struct amount_msat res; + + res.millisatoshis = msat.millisatoshis % MSAT_PER_SAT; + return res; +} + /* Different formatting by amounts: btc, sat and msat */ const char *fmt_amount_msat_btc(const tal_t *ctx, struct amount_msat msat, @@ -360,6 +368,27 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val, return true; } +WARN_UNUSED_RESULT bool amount_msat_add_sat_s64(struct amount_msat *val, + struct amount_msat a, + s64 b) +{ + if (b < 0) + return amount_msat_sub_sat(val, a, amount_sat(-b)); + else + return amount_msat_add_sat(val, a, amount_sat(b)); +} + + +WARN_UNUSED_RESULT bool amount_sat_add_sat_s64(struct amount_sat *val, + struct amount_sat a, + s64 b) +{ + if (b < 0) + return amount_sat_sub(val, a, amount_sat(-b)); + else + return amount_sat_add(val, a, amount_sat(b)); +} + bool amount_sat_eq(struct amount_sat a, struct amount_sat b) { return a.satoshis == b.satoshis; diff --git a/common/amount.h b/common/amount.h index de3dd5a3a764..a73be9dd8a0f 100644 --- a/common/amount.h +++ b/common/amount.h @@ -60,6 +60,9 @@ WARN_UNUSED_RESULT bool amount_msat_to_sat(struct amount_sat *sat, /* You can always truncate millisatoshis->satoshis. */ struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat); +/* The msats truncated by `amount_msat_to_sat_round_down` */ +struct amount_msat amount_msat_to_sat_remainder(struct amount_msat msat); + /* Simple operations: val = a + b, val = a - b. */ WARN_UNUSED_RESULT bool amount_msat_add(struct amount_msat *val, struct amount_msat a, @@ -89,6 +92,14 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val, struct amount_sat sat, double scale); +WARN_UNUSED_RESULT bool amount_msat_add_sat_s64(struct amount_msat *val, + struct amount_msat a, + s64 b); + +WARN_UNUSED_RESULT bool amount_sat_add_sat_s64(struct amount_sat *val, + struct amount_sat a, + s64 b); + struct amount_msat amount_msat_div(struct amount_msat msat, u64 div); struct amount_sat amount_sat_div(struct amount_sat sat, u64 div); diff --git a/common/bolt11.c b/common/bolt11.c index 825d850d69f1..616eab84ac41 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -419,7 +419,7 @@ static const char *decode_f(struct bolt11 *b11, } if (tal_count(f) > 40) { return tal_fmt(b11, - "f: witness v%ld bad length %zu", + "f: witness v%"PRIu64" bad length %zu", version, tal_count(f)); } diff --git a/common/features.c b/common/features.c index 8e0918db117c..89c1bd04746d 100644 --- a/common/features.c +++ b/common/features.c @@ -142,6 +142,10 @@ static const struct feature_style feature_styles[] = { { OPT_PROVIDE_PEER_BACKUP_STORAGE, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } }, + { OPT_SPLICE, + .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} }, }; struct dependency { @@ -467,7 +471,7 @@ const char *feature_name(const tal_t *ctx, size_t f) "option_trampoline_routing", /* https://github.com/lightning/bolts/pull/836 */ NULL, NULL, /* 60/61 */ - NULL, + "option_splice", NULL, NULL, NULL, diff --git a/common/features.h b/common/features.h index 768cd0f68f3a..31728d1f2758 100644 --- a/common/features.h +++ b/common/features.h @@ -99,7 +99,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx, #define COMPULSORY_FEATURE(x) ((x) & 0xFFFFFFFE) #define OPTIONAL_FEATURE(x) ((x) | 1) -/* BOLT #9: +/* BOLT-a526652801a541ed33b34d000a3b686a857c811f #9: * * | Bits | Name |... * | 0/1 | `option_data_loss_protect` |... IN ... @@ -118,6 +118,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx, * | 26/27 | `option_shutdown_anysegwit` |... IN ... * | 44/45 | `option_channel_type` |... IN ... * | 48/49 | `option_payment_metadata` |... 9 ... + * | 62/63 | `option_splice` |... IN ... */ #define OPT_DATA_LOSS_PROTECT 0 #define OPT_INITIAL_ROUTING_SYNC 2 @@ -135,6 +136,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx, #define OPT_SHUTDOWN_ANYSEGWIT 26 #define OPT_CHANNEL_TYPE 44 #define OPT_PAYMENT_METADATA 48 +#define OPT_SPLICE 62 /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9: * | 28/29 | `option_dual_fund` | ... IN9 ... diff --git a/common/gossip_constants.h b/common/gossip_constants.h index ebfa8ed58217..32218a45b25f 100644 --- a/common/gossip_constants.h +++ b/common/gossip_constants.h @@ -7,7 +7,7 @@ * only onion tlv payloads. */ #define ROUTING_MAX_HOPS 20 -/* BOLT #7: +/* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #7: * * The `channel_flags` bitfield...individual bits: *... diff --git a/common/hsm_version.h b/common/hsm_version.h index 0b58011b3006..8470dce64226 100644 --- a/common/hsm_version.h +++ b/common/hsm_version.h @@ -15,6 +15,7 @@ * v4 with sign_any_penalty_to_us: ead7963185194a515d1f14d2c44401392575299d68ce9a13d8a12baff3cf4f35 * v4 with sign_anchorspend: 8a30722e38b56e82af566b9629ff18da01fcebd1e80ec67f04d8b3a2fa66d81c * v4 with sign_htlc_tx_mingle: b9247e75d41ee1b3fc2f7db0bac8f4e92d544ab2f017d430ae3a000589c384e5 + * v4 with splicing: 06f21012936f825913af289fa81af1512c9ada1cb97c611698975a8fd287edbb */ #define HSM_MIN_VERSION 3 #define HSM_MAX_VERSION 4 diff --git a/common/initial_channel.c b/common/initial_channel.c index 2d4509c7c60f..8d073c32fe56 100644 --- a/common/initial_channel.c +++ b/common/initial_channel.c @@ -65,6 +65,11 @@ struct channel *new_initial_channel(const tal_t *ctx, = channel->view[LOCAL].owed[REMOTE] = remote_msatoshi; + channel->view[LOCAL].lowest_splice_amnt[LOCAL] = 0; + channel->view[LOCAL].lowest_splice_amnt[REMOTE] = 0; + channel->view[REMOTE].lowest_splice_amnt[LOCAL] = 0; + channel->view[REMOTE].lowest_splice_amnt[REMOTE] = 0; + channel->basepoints[LOCAL] = *local_basepoints; channel->basepoints[REMOTE] = *remote_basepoints; @@ -147,6 +152,34 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, return init_tx; } +const char *channel_update_funding(struct channel *channel, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + s64 splice_amnt) +{ + s64 funding_diff = (s64)funding_sats.satoshis - (s64)channel->funding_sats.satoshis; /* Raw: splicing */ + s64 remote_splice_amnt = funding_diff - splice_amnt; + + channel->funding = *funding; + channel->funding_sats = funding_sats; + + if (splice_amnt * 1000 + channel->view[LOCAL].owed[LOCAL].millisatoshis < 0) /* Raw: splicing */ + return tal_fmt(tmpctx, "Channel funding update would make local" + " balance negative."); + + channel->view[LOCAL].owed[LOCAL].millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + channel->view[REMOTE].owed[LOCAL].millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + + if (remote_splice_amnt * 1000 + channel->view[LOCAL].owed[REMOTE].millisatoshis < 0) /* Raw: splicing */ + return tal_fmt(tmpctx, "Channel funding update would make" + " remote balance negative."); + + channel->view[LOCAL].owed[REMOTE].millisatoshis += remote_splice_amnt * 1000; /* Raw: splicing */ + channel->view[REMOTE].owed[REMOTE].millisatoshis += remote_splice_amnt * 1000; /* Raw: splicing */ + + return NULL; +} + u32 channel_feerate(const struct channel *channel, enum side side) { return get_feerate(channel->fee_states, channel->opener, side); diff --git a/common/initial_channel.h b/common/initial_channel.h index 0ec7cf3aea96..3c20270e523e 100644 --- a/common/initial_channel.h +++ b/common/initial_channel.h @@ -16,8 +16,19 @@ struct fulfilled_htlc; /* View from each side */ struct channel_view { - /* How much is owed to each side (includes pending changes) */ + /* How much is owed to each side (includes pending changes). + * The index of `owed` array is always relative to the machine + * this code is running on, so REMOTE is always the other machine + * and LOCAL is always this machine (regardless of view). + * + * For example: + * view[REMOTE].owed[REMOTE] == view[LOCAL].owed[REMOTE] + * view[REMOTE].owed[LOCAL] == view[LOCAL].owed[LOCAL] + */ struct amount_msat owed[NUM_SIDES]; + /* Lowest splice relative change amount of all candidate splices. + * This will be 0 or negative -- never positive. */ + s64 lowest_splice_amnt[NUM_SIDES]; }; struct channel { @@ -135,6 +146,17 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, struct wally_tx_output *direct_outputs[NUM_SIDES], char** err_reason); +/* channel_update_funding: Changes the funding for the channel and updates the + * balance by the difference between `old_local_funding_msatoshi` and + * `new_local_funding_msatoshi`. + * + * Returns NULL on success or an error on failure. + */ +const char *channel_update_funding(struct channel *channel, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + s64 splice_amnt); + /** * channel_feerate: Get fee rate for this side of channel. * @channel: The channel diff --git a/common/interactivetx.c b/common/interactivetx.c index 8b63a9cb5052..b3cb21f18d51 100644 --- a/common/interactivetx.c +++ b/common/interactivetx.c @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include @@ -17,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +23,6 @@ #include #include #include -#include /* * BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2: @@ -64,14 +62,13 @@ struct interactivetx_context *new_interactivetx_context(const tal_t *ctx, { struct interactivetx_context *ictx = tal(ctx, struct interactivetx_context); - ictx->ctx = NULL; ictx->our_role = our_role; ictx->pps = pps; ictx->channel_id = channel_id; ictx->tx_add_input_count = 0; ictx->tx_add_output_count = 0; - ictx->next_update = default_next_update; - ictx->current_psbt = NULL; + ictx->next_update_fn = default_next_update; + ictx->current_psbt = create_psbt(ictx, 0, 0, 0); ictx->desired_psbt = NULL; ictx->pause_when_complete = false; ictx->change_set = NULL; @@ -102,26 +99,14 @@ static u8 *read_next_msg(const tal_t *ctx, for (;;) { char *desc; bool warning; - struct channel_id actual; enum peer_wire t; - bool from_gossipd; /* Prevent runaway memory usage from many messages */ if (msg) tal_free(msg); /* This helper routine polls the peer. */ - msg = peer_or_gossip_sync_read(ctx, state->pps, &from_gossipd); - - /* Line should be in STFU mode and not receiving gossip */ - if (from_gossipd) { - *error = tal_fmt(ctx, "interactivetx got gossip but" - " should be in STFU mode."); - - tal_free(msg); - /* Return NULL so caller knows to stop negotiating. */ - return NULL; - } + msg = peer_read(ctx, state->pps); /* BOLT #1: * @@ -140,7 +125,7 @@ static u8 *read_next_msg(const tal_t *ctx, if (!desc) continue; - *error = tal_fmt(ctx, "They sent a %s: %s" + *error = tal_fmt(ctx, "They sent a %s: %s", warning ? "warning" : "error", desc); @@ -160,9 +145,11 @@ static u8 *read_next_msg(const tal_t *ctx, case WIRE_TX_REMOVE_OUTPUT: case WIRE_TX_COMPLETE: return msg; + case WIRE_TX_ABORT: + /* TODO */ case WIRE_TX_SIGNATURES: - case WIRE_FUNDING_LOCKED: - case WIRE_INIT_RBF: + case WIRE_CHANNEL_READY: + case WIRE_TX_INIT_RBF: case WIRE_OPEN_CHANNEL2: case WIRE_INIT: case WIRE_ERROR: @@ -184,7 +171,7 @@ static u8 *read_next_msg(const tal_t *ctx, case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: - case WIRE_ACK_RBF: + case WIRE_TX_ACK_RBF: case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_CHANNEL_UPDATE: case WIRE_NODE_ANNOUNCEMENT: @@ -197,6 +184,11 @@ static u8 *read_next_msg(const tal_t *ctx, case WIRE_PONG: case WIRE_SHUTDOWN: case WIRE_STFU: + case WIRE_PEER_STORAGE: + case WIRE_YOUR_PEER_STORAGE: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: *error = tal_fmt(ctx, "Received invalid message from peer: %d", t); return NULL; @@ -211,7 +203,7 @@ static char *send_next(const tal_t *ctx, struct channel_id *cid = &ictx->channel_id; struct psbt_changeset *set = ictx->change_set; u64 serial_id; - u8 *msg = NULL; + u8 *msg; *finished = false; if (!set) @@ -219,35 +211,29 @@ static char *send_next(const tal_t *ctx, if (tal_count(set->added_ins) != 0) { const struct input_set *in = &set->added_ins[0]; - struct bitcoin_outpoint outpoint; u8 *prevtx; if (!psbt_get_serial_id(&in->input.unknowns, &serial_id)) - return "interactivetx ADD_INPUT PSBT has invalid serial_id."; + return "interactivetx ADD_INPUT PSBT has invalid" + " serial_id."; if (in->input.utxo) - prevtx = linearize_wtx(ctx, - in->input.utxo); + prevtx = linearize_wtx(ctx, in->input.utxo); else - return "interactivetx ADD_INPUT PSBT needs the previous transaction set."; - - memcpy(outpoint.txid.shad.sha.u.u8, - in->tx_input.txhash, - WALLY_TXHASH_LEN); - - outpoint.n = in->tx_input.index; + return "interactivetx ADD_INPUT PSBT needs the previous" + " transaction set."; msg = towire_tx_add_input(NULL, cid, serial_id, - prevtx, in->tx_input.index, - in->tx_input.sequence, - NULL); + prevtx, in->input.index, + in->input.sequence); tal_arr_remove(&set->added_ins, 0); } else if (tal_count(set->rm_ins) != 0) { if (!psbt_get_serial_id(&set->rm_ins[0].input.unknowns, &serial_id)) - return "interactivetx RM_INPUT PSBT has invalid serial_id."; + return "interactivetx RM_INPUT PSBT has invalid" + " serial_id."; msg = towire_tx_remove_input(NULL, cid, serial_id); @@ -262,11 +248,12 @@ static char *send_next(const tal_t *ctx, out = &set->added_outs[0]; if (!psbt_get_serial_id(&out->output.unknowns, &serial_id)) - return "interactivetx ADD_OUTPUT PSBT has invalid serial_id."; + return "interactivetx ADD_OUTPUT PSBT has invalid" + " serial_id."; - asset_amt = wally_tx_output_get_amount(&out->tx_output); + asset_amt = wally_psbt_output_get_amount(&out->output); sats = amount_asset_to_sat(&asset_amt); - script = wally_tx_output_get_script(ctx, &out->tx_output); + script = wally_psbt_output_get_script(ctx, &out->output); msg = towire_tx_add_output(NULL, cid, @@ -290,33 +277,74 @@ static char *send_next(const tal_t *ctx, if (!msg) return "Interactivetx send_next failed to build a message"; - sync_crypto_write(ictx->pps, take(msg)); + peer_write(ictx->pps, take(msg)); return NULL; tx_complete: - *finished = true; if (!ictx->pause_when_complete) { if (ictx->current_psbt->num_inputs > MAX_FUNDING_INPUTS) return tal_fmt(ctx, "Humbly refusing to `tx_complete` " "because we have too many inputs (%zu). " - "Limit is %zu." + "Limit is %d.", ictx->current_psbt->num_inputs, MAX_FUNDING_INPUTS); if (ictx->current_psbt->num_outputs > MAX_FUNDING_OUTPUTS) return tal_fmt(ctx, "Humbly refusing to `tx_complete` " "because we have too many outputs (%zu). " - "Limit is %zu." + "Limit is %d.", ictx->current_psbt->num_outputs, MAX_FUNDING_OUTPUTS); - msg = towire_tx_complete(ctx, cid); - sync_crypto_write(ictx->pps, msg); + msg = towire_tx_complete(NULL, cid); + peer_write(ictx->pps, take(msg)); } + *finished = true; return NULL; } +static struct psbt_changeset *get_changes(const tal_t *ctx, + struct interactivetx_context *ictx, + struct wally_psbt *next_psbt) +{ + u64 serial_id; + struct psbt_changeset *set = psbt_get_changeset(tmpctx, + ictx->current_psbt, + next_psbt); + + /* Remove duplicate serial_ids from the change set. */ + for (int i = 0; i < tal_count(set->added_ins); i++) { + struct bitcoin_outpoint point; + wally_psbt_input_get_outpoint(&set->added_ins[i].input, &point); + if (psbt_get_serial_id(&set->added_ins[i].input.unknowns, + &serial_id)) { + if (psbt_find_serial_input(ictx->current_psbt, + serial_id) != -1) + tal_arr_remove(&set->added_ins, i--); + else if (psbt_has_input(ictx->current_psbt, &point)) + tal_arr_remove(&set->added_ins, i--); + } + } + for (int i = 0; i < tal_count(set->added_outs); i++) + if (psbt_get_serial_id(&set->added_outs[i].output.unknowns, + &serial_id)) + if (psbt_find_serial_output(ictx->current_psbt, + serial_id) != -1) + tal_arr_remove(&set->added_outs, i--); + + return set; +} + +bool interactivetx_has_changes(struct interactivetx_context *ictx, + struct wally_psbt *next_psbt) +{ + struct psbt_changeset *set = get_changes(tmpctx, ictx, next_psbt); + + return tal_count(set->added_ins) || tal_count(set->rm_ins) + || tal_count(set->added_outs) || tal_count(set->rm_outs); +} + char *process_interactivetx_updates(const tal_t *ctx, struct interactivetx_context *ictx, bool *received_tx_complete) @@ -326,26 +354,21 @@ char *process_interactivetx_updates(const tal_t *ctx, char *error = NULL; struct wally_psbt *next_psbt; - if (ictx->current_psbt == NULL) - ictx->current_psbt = create_psbt(ictx, 0, 0, 0); - if (received_tx_complete) they_complete = *received_tx_complete; /* Build change_set and handle PSBT variables */ ictx->change_set = tal_free(ictx->change_set); - /* Call next_update or default to 'desired_psbt' */ - next_psbt = ictx->next_update(ictx, ictx); + /* Call next_update_fn or default to 'desired_psbt' */ + next_psbt = ictx->next_update_fn(ictx, ictx); - /* Returning NULL from next_update is the same as using `current_psbt` + /* Returning NULL from next_update_fn is the same as using `current_psbt` * with no changes -- both indicate no changes */ if (!next_psbt) next_psbt = ictx->current_psbt; - ictx->change_set = psbt_get_changeset(ictx, - ictx->current_psbt, - next_psbt); + ictx->change_set = get_changes(ctx, ictx, next_psbt); /* If current_psbt and next_psbt are the same, dont double free it! * Otherwise we advance `current_psbt` to `next_psbt` and begin @@ -353,7 +376,7 @@ char *process_interactivetx_updates(const tal_t *ctx, if (ictx->current_psbt != next_psbt) { /* psbt_get_changeset requires we keep the current_psbt until * we're done withh change_set */ - tal_steal(ictx->change_set, current_psbt); + tal_steal(ictx->change_set, ictx->current_psbt); ictx->current_psbt = next_psbt; } @@ -389,7 +412,7 @@ char *process_interactivetx_updates(const tal_t *ctx, t = fromwire_peektype(msg); switch (t) { case WIRE_TX_ADD_INPUT: { - const u8 *tx_bytes, *redeemscript; + const u8 *tx_bytes; u32 sequence; size_t len; struct bitcoin_tx *tx; @@ -399,9 +422,7 @@ char *process_interactivetx_updates(const tal_t *ctx, &serial_id, cast_const2(u8 **, &tx_bytes), - &outpoint.n, &sequence, - cast_const2(u8 **, - &redeemscript))) + &outpoint.n, &sequence)) return tal_fmt(ctx, "Parsing tx_add_input %s", tal_hex(ctx, msg)); @@ -435,7 +456,7 @@ char *process_interactivetx_updates(const tal_t *ctx, * the transaction */ if (psbt_find_serial_input(ictx->current_psbt, serial_id) != -1) - return tal_fmt(ctx, "Duplicate serial_id rcvd." + return tal_fmt(ctx, "Duplicate serial_id rcvd" " %"PRIu64, serial_id); /* Convert tx_bytes to a tx! */ @@ -459,7 +480,7 @@ char *process_interactivetx_updates(const tal_t *ctx, */ if (!is_segwit_output(ctx, &tx->wtx->outputs[outpoint.n], - redeemscript)) + NULL)) return tal_fmt(ctx, "Invalid tx sent. Not SegWit %s", type_to_string(ctx, @@ -491,7 +512,7 @@ char *process_interactivetx_updates(const tal_t *ctx, */ if (ictx->current_psbt->num_inputs + 1 > MAX_FUNDING_INPUTS) return tal_fmt(ctx, "Too many inputs. Have %zu," - " Max allowed %zu", + " Max allowed %d", ictx->current_psbt->num_inputs + 1, MAX_FUNDING_INPUTS); @@ -618,7 +639,7 @@ char *process_interactivetx_updates(const tal_t *ctx, */ if (ictx->current_psbt->num_outputs + 1 > MAX_FUNDING_OUTPUTS) return tal_fmt(ctx, "Too many inputs. Have %zu," - " Max allowed %zu", + " Max allowed %d", ictx->current_psbt->num_outputs + 1, MAX_FUNDING_OUTPUTS); @@ -676,6 +697,8 @@ char *process_interactivetx_updates(const tal_t *ctx, if (received_tx_complete) *received_tx_complete = true; break; + case WIRE_TX_ABORT: + /* Todo */ case WIRE_INIT: case WIRE_ERROR: case WIRE_WARNING: @@ -683,7 +706,7 @@ char *process_interactivetx_updates(const tal_t *ctx, case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: case WIRE_UPDATE_ADD_HTLC: @@ -697,13 +720,12 @@ char *process_interactivetx_updates(const tal_t *ctx, case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: case WIRE_TX_SIGNATURES: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: - case WIRE_INIT_RBF: - case WIRE_ACK_RBF: + case WIRE_TX_INIT_RBF: + case WIRE_TX_ACK_RBF: case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_CHANNEL_UPDATE: case WIRE_NODE_ANNOUNCEMENT: @@ -713,9 +735,12 @@ char *process_interactivetx_updates(const tal_t *ctx, case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_PING: case WIRE_PONG: - case WIRE_STFU: + case WIRE_PEER_STORAGE: + case WIRE_YOUR_PEER_STORAGE: case WIRE_SPLICE: case WIRE_SPLICE_ACK: + case WIRE_STFU: + case WIRE_SPLICE_LOCKED: return tal_fmt(ctx, "Unexpected wire message %s", tal_hex(ctx, msg)); } @@ -727,5 +752,7 @@ char *process_interactivetx_updates(const tal_t *ctx, /* Sort psbt! */ psbt_sort_by_serial_id(ictx->current_psbt); + tal_steal(ictx, ictx->current_psbt); + return NULL; } diff --git a/common/interactivetx.h b/common/interactivetx.h index 0d8a27588947..a63d01896dd9 100644 --- a/common/interactivetx.h +++ b/common/interactivetx.h @@ -26,9 +26,6 @@ enum tx_msgs { struct interactivetx_context { - /* Users can set this to their own context */ - void *ctx; - enum tx_role our_role; struct per_peer_state *pps; struct channel_id channel_id; @@ -45,16 +42,14 @@ struct interactivetx_context { * If no more changes are demanded, return NULL or current_psbt * unchanged to signal completion. */ - struct wally_psbt *(*next_update)(const tal_t *ctx, + struct wally_psbt *(*next_update_fn)(const tal_t *ctx, struct interactivetx_context *ictx); - /* Set this to the intial psbt. If NULL will be filled with an empty - * psbt. - */ + /* Set this to the intial psbt. Defaults to an empty PSBT. */ struct wally_psbt *current_psbt; /* Optional field for storing your side's desired psbt state, to be - * used inside 'next_update'. + * used inside 'next_update_fn'. */ struct wally_psbt *desired_psbt; @@ -74,8 +69,8 @@ struct interactivetx_context *new_interactivetx_context(const tal_t *ctx, struct channel_id channel_id); /* Blocks the thread until we run out of changes (and we send tx_complete), - * or an error occurs. If 'pause_when_complete' is set, this behavior changes - * and we return without sending tx_complete. + * or an error occurs. If 'pause_when_complete' on the `interactivetx_context` + * is set, this behavior changes and we return without sending tx_complete. * * If received_tx_complete is not NULL: * in -> true means we assume we've received tx_complete in a previous round. @@ -87,4 +82,11 @@ char *process_interactivetx_updates(const tal_t *ctx, struct interactivetx_context *ictx, bool *received_tx_complete); +/* If the given ictx would cause `process_interactivetx_updates to send tx + * changes when called. Returns true if an error occurs + * (call `process_interactivetx_updates` for a description of the error). + */ +bool interactivetx_has_changes(struct interactivetx_context *ictx, + struct wally_psbt *next_psbt); + #endif /* LIGHTNING_COMMON_INTERACTIVETX_H */ diff --git a/common/json_param.c b/common/json_param.c index d47c116c1b73..ceda9c7c3178 100644 --- a/common/json_param.c +++ b/common/json_param.c @@ -503,6 +503,18 @@ struct command_result *param_u64(struct command *cmd, const char *name, "should be an unsigned 64 bit integer"); } +struct command_result *param_s64(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + int64_t **num) +{ + *num = tal(cmd, int64_t); + if (json_to_s64(buffer, tok, *num)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be an sign 64 bit integer"); +} + struct command_result *param_msat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct amount_msat **msat) diff --git a/common/json_param.h b/common/json_param.h index a81aa7c63c75..f920791cd90a 100644 --- a/common/json_param.h +++ b/common/json_param.h @@ -212,6 +212,11 @@ struct command_result *param_u64(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, uint64_t **num); +/* Extract number from this (may be a string, or a number literal) */ +struct command_result *param_s64(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + int64_t **num); + /* Extract msatoshi amount from this string */ struct command_result *param_msat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index ee4f4d6d481e..30163d1790a6 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -65,6 +65,15 @@ enum jsonrpc_errcode { FUNDING_STATE_INVALID = 312, FUND_CANNOT_AFFORD_WITH_EMERGENCY = 313, + /* Splice errors */ + SPLICE_BROADCAST_FAIL = 350, + SPLICE_WRONG_OWNER = 351, + SPLICE_UNKNOWN_CHANNEL = 352, + SPLICE_INVALID_CHANNEL_STATE = 353, + SPLICE_NOT_SUPPORTED = 354, + SPLICE_BUSY_ERROR = 355, + SPLICE_INPUT_ERROR = 356, + /* `connect` errors */ CONNECT_NO_KNOWN_ADDRESS = 400, CONNECT_ALL_ADDRESSES_FAILED = 401, diff --git a/common/psbt_internal.c b/common/psbt_internal.c index 454e824fbb89..aca36b6ad96a 100644 --- a/common/psbt_internal.c +++ b/common/psbt_internal.c @@ -116,7 +116,8 @@ void psbt_finalize_input(const tal_t *ctx, const struct witness ** psbt_to_witnesses(const tal_t *ctx, const struct wally_psbt *psbt, - enum tx_role side_to_stack) + enum tx_role side_to_stack, + int input_index_to_ignore) { u64 serial_id; const struct witness **witnesses = @@ -128,6 +129,9 @@ psbt_to_witnesses(const tal_t *ctx, /* FIXME: throw an error ? */ return tal_free(witnesses); + if (input_index_to_ignore == i) + continue; + /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2: * - if is the *initiator*: * - MUST send even `serial_id`s diff --git a/common/psbt_internal.h b/common/psbt_internal.h index 166c7a91f6b6..9b172505c9e5 100644 --- a/common/psbt_internal.h +++ b/common/psbt_internal.h @@ -21,16 +21,18 @@ void psbt_finalize_input(const tal_t *ctx, struct wally_psbt_input *in, const struct witness *witness); -/* psbt_to_witness_stacks - Take a side's sigs from a PSBT and copy to a +/* psbt_to_witnesses - Take a side's sigs from a PSBT and copy to a * wire witness * * @ctx - allocation context * @psbt - PSBT to copy sigs from * @side_to_stack - which side to stack witnesses of + * @input_index_to_ignore - which input to not include. Pass -1 to include all. */ const struct witness ** psbt_to_witnesses(const tal_t *ctx, const struct wally_psbt *psbt, - enum tx_role side_to_stack); + enum tx_role side_to_stack, + int input_index_to_ignore); #endif /* LIGHTNING_COMMON_PSBT_INTERNAL_H */ diff --git a/common/psbt_open.c b/common/psbt_open.c index 1bc250cc7fff..fbd2e9c40578 100644 --- a/common/psbt_open.c +++ b/common/psbt_open.c @@ -80,6 +80,8 @@ static const u8 *linearize_input(const tal_t *ctx, psbt->inputs[0].taproot_leaf_paths.num_items = 0; psbt->inputs[0].keypaths.num_items = 0; psbt->inputs[0].signatures.num_items = 0; + psbt->inputs[0].utxo = NULL; + psbt->inputs[0].witness_utxo = NULL; const u8 *bytes = psbt_get_bytes(ctx, psbt, &byte_len); diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index bcfc9ee6b0bc..b7519fb7f4f7 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -45,9 +45,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_s64 */ -void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) -{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_secp256k1_ecdsa_signature */ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, const secp256k1_ecdsa_signature *signature UNNEEDED) @@ -70,6 +67,9 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) /* Generated stub for towire_u64 */ void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) { fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_s64 */ +void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) +{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_u8 */ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) { fprintf(stderr, "towire_u8 called!\n"); abort(); } diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index 64e246e00085..526611768a79 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -87,6 +87,9 @@ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u64 */ u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_s64 */ +s64 fromwire_s64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_s64 called!\n"); abort(); } /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } @@ -184,6 +187,9 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) /* Generated stub for towire_u64 */ void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) { fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_s64 */ +void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) +{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_u8 */ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) { fprintf(stderr, "towire_u8 called!\n"); abort(); } diff --git a/connectd/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c index 93a58fe3cd1f..f977ca1a7811 100644 --- a/connectd/gossip_rcvd_filter.c +++ b/connectd/gossip_rcvd_filter.c @@ -92,6 +92,9 @@ static bool is_msg_gossip_broadcast(const u8 *cursor) case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: break; } return false; diff --git a/connectd/gossip_store.c b/connectd/gossip_store.c index 78af959c783f..ca1fdd4deec5 100644 --- a/connectd/gossip_store.c +++ b/connectd/gossip_store.c @@ -95,6 +95,9 @@ static bool public_msg_type(enum peer_wire type) case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: return false; case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_NODE_ANNOUNCEMENT: diff --git a/connectd/multiplex.c b/connectd/multiplex.c index a79ddf53109d..ce69b1dcc997 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -390,6 +390,9 @@ static bool is_urgent(enum peer_wire type) case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: return false; /* These are time-sensitive, and so send without delay. */ diff --git a/contrib/msggen/msggen/gen/grpc2py.py b/contrib/msggen/msggen/gen/grpc2py.py index 9cf03c609f5c..1a0c159ed143 100644 --- a/contrib/msggen/msggen/gen/grpc2py.py +++ b/contrib/msggen/msggen/gen/grpc2py.py @@ -44,6 +44,7 @@ def __init__(self, dest: TextIO): 's16': "m.{name}", 's32': "m.{name}", 's64': "m.{name}", + 'integer': "m.{name}", 'boolean': "m.{name}", 'short_channel_id': "m.{name}", 'msat': "amount2msat(m.{name})", diff --git a/contrib/msggen/msggen/gen/rust.py b/contrib/msggen/msggen/gen/rust.py index 94b999306fed..44072d808556 100644 --- a/contrib/msggen/msggen/gen/rust.py +++ b/contrib/msggen/msggen/gen/rust.py @@ -35,6 +35,7 @@ 'outputdesc': 'OutputDesc', 'hash': 'Sha256', 'secret': 'Secret', + 'integer': 'i64', } header = f"""#![allow(non_camel_case_types)] diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index c709f08778ff..b535f88e9bab 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -1212,6 +1212,32 @@ def openchannel_abort(self, channel_id): } return self.call("openchannel_abort", payload) + def splice_init(self, chan_id, amount, initialpsbt=None, feerate_per_kw=None): + """ Initiate a splice """ + payload = { + "channel_id": chan_id, + "relative_amount": amount, + "initialpsbt": initialpsbt, + "feerate_per_kw": feerate_per_kw, + } + return self.call("splice_init", payload) + + def splice_update(self, chan_id, psbt): + """ Update a splice """ + payload = { + "channel_id": chan_id, + "psbt": psbt + } + return self.call("splice_update", payload) + + def splice_signed(self, chan_id, psbt): + """ Initiate a splice """ + payload = { + "channel_id": chan_id, + "psbt": psbt + } + return self.call("splice_signed", payload) + def paystatus(self, bolt11=None): """Detail status of attempts to pay {bolt11} or any.""" payload = { diff --git a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py index 48821f4132dd..94c5aa99f510 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\"\xd6\x17\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\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\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\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\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\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\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\"\xaf\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\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\t\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\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\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\"\xfe\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\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\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x0b\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\x13\n\x11_payment_preimage\"\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\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xf4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\t\n\x07_partidB\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\"\xef\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\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\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\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\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xe3\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\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\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\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\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\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\"w\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\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x02\x88\x01\x01\x42\x07\n\x05_p2trB\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\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\"\xfd\x18\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\x1e\n\x11ignore_fee_limits\x18\x36 \x01(\x08H\x04\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x05\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x08\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\t\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\n\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\x0c\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\x0e\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x0f\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x10\x88\x01\x01\x12%\n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSideH\x11\x88\x01\x01\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\x12\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x13\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x19\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x1e\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH\x1f\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(\tH/\x88\x01\x01\"\xa3\x02\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\nB\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\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\"]\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\"\xd2\x02\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*\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x06 \x01(\x0cH\x05\x88\x01\x01\x42\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\n\n\x08_feerateB\x15\n\x13_total_funding_msatB\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\"\xc6\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\"D\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\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\"\xaa!\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\"l\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\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_hex\"<\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\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\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\"\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\x19PreapproveinvoiceResponse2\xc8\x1c\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\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\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\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\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\"\xd6\x17\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\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\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\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\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\"\xaf\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\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\t\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\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\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\"\xfe\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\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\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x0b\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\x13\n\x11_payment_preimage\"\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\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xf4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\t\n\x07_partidB\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\"\xef\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\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\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\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\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xe3\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\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\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\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\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\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\"w\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\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x02\x88\x01\x01\x42\x07\n\x05_p2trB\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\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\"\x9b\x19\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\x1e\n\x11ignore_fee_limits\x18\x36 \x01(\x08H\x04\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x05\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x08\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\t\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\n\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\x0c\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\x0e\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x0f\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x10\x88\x01\x01\x12%\n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSideH\x11\x88\x01\x01\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\x12\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x13\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x19\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x1e\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH\x1f\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(\tH/\x88\x01\x01\"\xc1\x02\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\x42\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\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\"]\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\"\xc6\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\"D\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\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\"\xaa!\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\"l\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\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_hex\"<\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\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\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\"\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\x19PreapproveinvoiceResponse2\xc8\x1c\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\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\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\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\x62\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'node_pb2', globals()) @@ -52,387 +52,387 @@ _LISTPEERSPEERSCHANNELSFEERATE._serialized_start=5009 _LISTPEERSPEERSCHANNELSFEERATE._serialized_end=5070 _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_start=5073 - _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_end=5270 - _LISTPEERSPEERSCHANNELSFUNDING._serialized_start=5273 - _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5556 - _LISTPEERSPEERSCHANNELSALIAS._serialized_start=5558 - _LISTPEERSPEERSCHANNELSALIAS._serialized_end=5649 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5652 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=6021 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5937 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=5992 - _LISTFUNDSREQUEST._serialized_start=6023 - _LISTFUNDSREQUEST._serialized_end=6071 - _LISTFUNDSRESPONSE._serialized_start=6073 - _LISTFUNDSRESPONSE._serialized_end=6174 - _LISTFUNDSOUTPUTS._serialized_start=6177 - _LISTFUNDSOUTPUTS._serialized_end=6564 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=6438 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=6519 - _LISTFUNDSCHANNELS._serialized_start=6567 - _LISTFUNDSCHANNELS._serialized_end=6866 - _SENDPAYREQUEST._serialized_start=6869 - _SENDPAYREQUEST._serialized_end=7218 - _SENDPAYRESPONSE._serialized_start=7221 - _SENDPAYRESPONSE._serialized_end=7814 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7635 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7677 - _SENDPAYROUTE._serialized_start=7816 - _SENDPAYROUTE._serialized_end=7908 - _LISTCHANNELSREQUEST._serialized_start=7911 - _LISTCHANNELSREQUEST._serialized_end=8058 - _LISTCHANNELSRESPONSE._serialized_start=8060 - _LISTCHANNELSRESPONSE._serialized_end=8127 - _LISTCHANNELSCHANNELS._serialized_start=8130 - _LISTCHANNELSCHANNELS._serialized_end=8565 - _ADDGOSSIPREQUEST._serialized_start=8567 - _ADDGOSSIPREQUEST._serialized_end=8602 - _ADDGOSSIPRESPONSE._serialized_start=8604 - _ADDGOSSIPRESPONSE._serialized_end=8623 - _AUTOCLEANINVOICEREQUEST._serialized_start=8625 - _AUTOCLEANINVOICEREQUEST._serialized_end=8736 - _AUTOCLEANINVOICERESPONSE._serialized_start=8739 - _AUTOCLEANINVOICERESPONSE._serialized_end=8868 - _CHECKMESSAGEREQUEST._serialized_start=8870 - _CHECKMESSAGEREQUEST._serialized_end=8955 - _CHECKMESSAGERESPONSE._serialized_start=8957 - _CHECKMESSAGERESPONSE._serialized_end=9013 - _CLOSEREQUEST._serialized_start=9016 - _CLOSEREQUEST._serialized_end=9347 - _CLOSERESPONSE._serialized_start=9350 - _CLOSERESPONSE._serialized_end=9521 - _CLOSERESPONSE_CLOSETYPE._serialized_start=9452 - _CLOSERESPONSE_CLOSETYPE._serialized_end=9505 - _CONNECTREQUEST._serialized_start=9523 - _CONNECTREQUEST._serialized_end=9607 - _CONNECTRESPONSE._serialized_start=9610 - _CONNECTRESPONSE._serialized_end=9790 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9755 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9790 - _CONNECTADDRESS._serialized_start=9793 - _CONNECTADDRESS._serialized_end=10044 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9932 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=10012 - _CREATEINVOICEREQUEST._serialized_start=10046 - _CREATEINVOICEREQUEST._serialized_end=10120 - _CREATEINVOICERESPONSE._serialized_start=10123 - _CREATEINVOICERESPONSE._serialized_end=10810 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10585 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10641 - _DATASTOREREQUEST._serialized_start=10813 - _DATASTOREREQUEST._serialized_end=11121 - _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10966 - _DATASTOREREQUEST_DATASTOREMODE._serialized_end=11078 - _DATASTORERESPONSE._serialized_start=11124 - _DATASTORERESPONSE._serialized_end=11254 - _CREATEONIONREQUEST._serialized_start=11257 - _CREATEONIONREQUEST._serialized_end=11414 - _CREATEONIONRESPONSE._serialized_start=11416 - _CREATEONIONRESPONSE._serialized_end=11476 - _CREATEONIONHOPS._serialized_start=11478 - _CREATEONIONHOPS._serialized_end=11528 - _DELDATASTOREREQUEST._serialized_start=11530 - _DELDATASTOREREQUEST._serialized_end=11604 - _DELDATASTORERESPONSE._serialized_start=11607 - _DELDATASTORERESPONSE._serialized_end=11740 - _DELEXPIREDINVOICEREQUEST._serialized_start=11742 - _DELEXPIREDINVOICEREQUEST._serialized_end=11814 - _DELEXPIREDINVOICERESPONSE._serialized_start=11816 - _DELEXPIREDINVOICERESPONSE._serialized_end=11843 - _DELINVOICEREQUEST._serialized_start=11846 - _DELINVOICEREQUEST._serialized_end=12028 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11962 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=12015 - _DELINVOICERESPONSE._serialized_start=12031 - _DELINVOICERESPONSE._serialized_end=12576 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11962 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=12015 - _INVOICEREQUEST._serialized_start=12579 - _INVOICEREQUEST._serialized_end=12829 - _INVOICERESPONSE._serialized_start=12832 - _INVOICERESPONSE._serialized_end=13237 - _LISTDATASTOREREQUEST._serialized_start=13239 - _LISTDATASTOREREQUEST._serialized_end=13274 - _LISTDATASTORERESPONSE._serialized_start=13276 - _LISTDATASTORERESPONSE._serialized_end=13347 - _LISTDATASTOREDATASTORE._serialized_start=13350 - _LISTDATASTOREDATASTORE._serialized_end=13485 - _LISTINVOICESREQUEST._serialized_start=13488 - _LISTINVOICESREQUEST._serialized_end=13838 - _LISTINVOICESREQUEST_LISTINVOICESINDEX._serialized_start=13709 - _LISTINVOICESREQUEST_LISTINVOICESINDEX._serialized_end=13754 - _LISTINVOICESRESPONSE._serialized_start=13840 - _LISTINVOICESRESPONSE._serialized_end=13907 - _LISTINVOICESINVOICES._serialized_start=13910 - _LISTINVOICESINVOICES._serialized_end=14676 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=14410 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=14473 - _SENDONIONREQUEST._serialized_start=14679 - _SENDONIONREQUEST._serialized_end=15073 - _SENDONIONRESPONSE._serialized_start=15076 - _SENDONIONRESPONSE._serialized_end=15599 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=15447 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=15491 - _SENDONIONFIRST_HOP._serialized_start=15601 - _SENDONIONFIRST_HOP._serialized_end=15682 - _LISTSENDPAYSREQUEST._serialized_start=15685 - _LISTSENDPAYSREQUEST._serialized_end=15920 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=15822 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=15881 - _LISTSENDPAYSRESPONSE._serialized_start=15922 - _LISTSENDPAYSRESPONSE._serialized_end=15989 - _LISTSENDPAYSPAYMENTS._serialized_start=15992 - _LISTSENDPAYSPAYMENTS._serialized_end=16620 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=16426 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=16493 - _LISTTRANSACTIONSREQUEST._serialized_start=16622 - _LISTTRANSACTIONSREQUEST._serialized_end=16647 - _LISTTRANSACTIONSRESPONSE._serialized_start=16649 - _LISTTRANSACTIONSRESPONSE._serialized_end=16732 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=16735 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16983 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16985 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=17068 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=17070 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17178 - _PAYREQUEST._serialized_start=17181 - _PAYREQUEST._serialized_end=17655 - _PAYRESPONSE._serialized_start=17658 - _PAYRESPONSE._serialized_end=18037 - _PAYRESPONSE_PAYSTATUS._serialized_start=17940 - _PAYRESPONSE_PAYSTATUS._serialized_end=17990 - _LISTNODESREQUEST._serialized_start=18039 - _LISTNODESREQUEST._serialized_end=18081 - _LISTNODESRESPONSE._serialized_start=18083 - _LISTNODESRESPONSE._serialized_end=18138 - _LISTNODESNODES._serialized_start=18141 - _LISTNODESNODES._serialized_end=18366 - _LISTNODESNODESADDRESSES._serialized_start=18369 - _LISTNODESNODESADDRESSES._serialized_end=18601 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18509 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18589 - _WAITANYINVOICEREQUEST._serialized_start=18603 - _WAITANYINVOICEREQUEST._serialized_end=18706 - _WAITANYINVOICERESPONSE._serialized_start=18709 - _WAITANYINVOICERESPONSE._serialized_end=19332 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19141 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19186 - _WAITINVOICEREQUEST._serialized_start=19334 - _WAITINVOICEREQUEST._serialized_end=19369 - _WAITINVOICERESPONSE._serialized_start=19372 - _WAITINVOICERESPONSE._serialized_end=19983 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19795 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19837 - _WAITSENDPAYREQUEST._serialized_start=19986 - _WAITSENDPAYREQUEST._serialized_end=20128 - _WAITSENDPAYRESPONSE._serialized_start=20131 - _WAITSENDPAYRESPONSE._serialized_end=20693 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20535 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20568 - _NEWADDRREQUEST._serialized_start=20696 - _NEWADDRREQUEST._serialized_end=20847 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20780 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20831 - _NEWADDRRESPONSE._serialized_start=20849 - _NEWADDRRESPONSE._serialized_end=20968 - _WITHDRAWREQUEST._serialized_start=20971 - _WITHDRAWREQUEST._serialized_end=21173 - _WITHDRAWRESPONSE._serialized_start=21175 - _WITHDRAWRESPONSE._serialized_end=21233 - _KEYSENDREQUEST._serialized_start=21236 - _KEYSENDREQUEST._serialized_end=21622 - _KEYSENDRESPONSE._serialized_start=21625 - _KEYSENDRESPONSE._serialized_end=21995 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21919 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21948 - _FUNDPSBTREQUEST._serialized_start=21998 - _FUNDPSBTREQUEST._serialized_end=22418 - _FUNDPSBTRESPONSE._serialized_start=22421 - _FUNDPSBTRESPONSE._serialized_end=22638 - _FUNDPSBTRESERVATIONS._serialized_start=22640 - _FUNDPSBTRESERVATIONS._serialized_end=22757 - _SENDPSBTREQUEST._serialized_start=22759 - _SENDPSBTREQUEST._serialized_end=22824 - _SENDPSBTRESPONSE._serialized_start=22826 - _SENDPSBTRESPONSE._serialized_end=22870 - _SIGNPSBTREQUEST._serialized_start=22872 - _SIGNPSBTREQUEST._serialized_end=22921 - _SIGNPSBTRESPONSE._serialized_start=22923 - _SIGNPSBTRESPONSE._serialized_end=22962 - _UTXOPSBTREQUEST._serialized_start=22965 - _UTXOPSBTREQUEST._serialized_end=23376 - _UTXOPSBTRESPONSE._serialized_start=23379 - _UTXOPSBTRESPONSE._serialized_end=23596 - _UTXOPSBTRESERVATIONS._serialized_start=23598 - _UTXOPSBTRESERVATIONS._serialized_end=23715 - _TXDISCARDREQUEST._serialized_start=23717 - _TXDISCARDREQUEST._serialized_end=23749 - _TXDISCARDRESPONSE._serialized_start=23751 - _TXDISCARDRESPONSE._serialized_end=23805 - _TXPREPAREREQUEST._serialized_start=23808 - _TXPREPAREREQUEST._serialized_end=23972 - _TXPREPARERESPONSE._serialized_start=23974 - _TXPREPARERESPONSE._serialized_end=24042 - _TXSENDREQUEST._serialized_start=24044 - _TXSENDREQUEST._serialized_end=24073 - _TXSENDRESPONSE._serialized_start=24075 - _TXSENDRESPONSE._serialized_end=24131 - _LISTPEERCHANNELSREQUEST._serialized_start=24133 - _LISTPEERCHANNELSREQUEST._serialized_end=24182 - _LISTPEERCHANNELSRESPONSE._serialized_start=24184 - _LISTPEERCHANNELSRESPONSE._serialized_end=24259 - _LISTPEERCHANNELSCHANNELS._serialized_start=24262 - _LISTPEERCHANNELSCHANNELS._serialized_end=27459 - _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_start=26253 - _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_end=26544 - _LISTPEERCHANNELSCHANNELSFEERATE._serialized_start=27461 - _LISTPEERCHANNELSCHANNELSFEERATE._serialized_end=27554 - _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_start=27557 - _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_end=27895 - _LISTPEERCHANNELSCHANNELSFUNDING._serialized_start=27898 - _LISTPEERCHANNELSCHANNELSFUNDING._serialized_end=28236 - _LISTPEERCHANNELSCHANNELSALIAS._serialized_start=28238 - _LISTPEERCHANNELSCHANNELSALIAS._serialized_end=28331 - _LISTPEERCHANNELSCHANNELSHTLCS._serialized_start=28334 - _LISTPEERCHANNELSCHANNELSHTLCS._serialized_end=28816 - _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_start=28655 - _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_end=28712 - _LISTCLOSEDCHANNELSREQUEST._serialized_start=28818 - _LISTCLOSEDCHANNELSREQUEST._serialized_end=28869 - _LISTCLOSEDCHANNELSRESPONSE._serialized_start=28871 - _LISTCLOSEDCHANNELSRESPONSE._serialized_end=28962 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_start=28965 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_end=30167 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_start=29865 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_end=29983 - _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_start=30169 - _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_end=30270 - _DECODEPAYREQUEST._serialized_start=30272 - _DECODEPAYREQUEST._serialized_end=30348 - _DECODEPAYRESPONSE._serialized_start=30351 - _DECODEPAYRESPONSE._serialized_end=30876 - _DECODEPAYFALLBACKS._serialized_start=30879 - _DECODEPAYFALLBACKS._serialized_end=31077 - _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_start=31000 - _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_end=31068 - _DECODEPAYEXTRA._serialized_start=31079 - _DECODEPAYEXTRA._serialized_end=31122 - _DECODEREQUEST._serialized_start=31124 - _DECODEREQUEST._serialized_end=31155 - _DECODERESPONSE._serialized_start=31158 - _DECODERESPONSE._serialized_end=35424 - _DECODERESPONSE_DECODETYPE._serialized_start=33726 - _DECODERESPONSE_DECODETYPE._serialized_end=33834 - _DECODEOFFER_PATHS._serialized_start=35426 - _DECODEOFFER_PATHS._serialized_end=35486 - _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_start=35489 - _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_end=35627 - _DECODEINVOICE_PATHSPATH._serialized_start=35629 - _DECODEINVOICE_PATHSPATH._serialized_end=35713 - _DECODEINVOICE_FALLBACKS._serialized_start=35715 - _DECODEINVOICE_FALLBACKS._serialized_end=35804 - _DECODEFALLBACKS._serialized_start=35806 - _DECODEFALLBACKS._serialized_end=35925 - _DECODEEXTRA._serialized_start=35927 - _DECODEEXTRA._serialized_end=35967 - _DECODERESTRICTIONS._serialized_start=35969 - _DECODERESTRICTIONS._serialized_end=36028 - _DISCONNECTREQUEST._serialized_start=36030 - _DISCONNECTREQUEST._serialized_end=36091 - _DISCONNECTRESPONSE._serialized_start=36093 - _DISCONNECTRESPONSE._serialized_end=36113 - _FEERATESREQUEST._serialized_start=36115 - _FEERATESREQUEST._serialized_end=36222 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=36185 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=36222 - _FEERATESRESPONSE._serialized_start=36225 - _FEERATESRESPONSE._serialized_end=36509 - _FEERATESPERKB._serialized_start=36512 - _FEERATESPERKB._serialized_end=36979 - _FEERATESPERKBESTIMATES._serialized_start=36982 - _FEERATESPERKBESTIMATES._serialized_end=37132 - _FEERATESPERKW._serialized_start=37135 - _FEERATESPERKW._serialized_end=37602 - _FEERATESPERKWESTIMATES._serialized_start=37605 - _FEERATESPERKWESTIMATES._serialized_end=37755 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=37758 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=38041 - _FUNDCHANNELREQUEST._serialized_start=38044 - _FUNDCHANNELREQUEST._serialized_end=38529 - _FUNDCHANNELRESPONSE._serialized_start=38532 - _FUNDCHANNELRESPONSE._serialized_end=38687 - _GETROUTEREQUEST._serialized_start=38690 - _GETROUTEREQUEST._serialized_end=38926 - _GETROUTERESPONSE._serialized_start=38928 - _GETROUTERESPONSE._serialized_end=38981 - _GETROUTEROUTE._serialized_start=38984 - _GETROUTEROUTE._serialized_end=39181 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=39152 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=39181 - _LISTFORWARDSREQUEST._serialized_start=39184 - _LISTFORWARDSREQUEST._serialized_end=39442 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=39324 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=39400 - _LISTFORWARDSRESPONSE._serialized_start=39444 - _LISTFORWARDSRESPONSE._serialized_end=39511 - _LISTFORWARDSFORWARDS._serialized_start=39514 - _LISTFORWARDSFORWARDS._serialized_end=40120 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=39903 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=39987 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=39989 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=40037 - _LISTPAYSREQUEST._serialized_start=40123 - _LISTPAYSREQUEST._serialized_end=40342 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=40248 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=40303 - _LISTPAYSRESPONSE._serialized_start=40344 - _LISTPAYSRESPONSE._serialized_end=40395 - _LISTPAYSPAYS._serialized_start=40398 - _LISTPAYSPAYS._serialized_end=41037 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=40812 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=40871 - _LISTHTLCSREQUEST._serialized_start=41039 - _LISTHTLCSREQUEST._serialized_end=41081 - _LISTHTLCSRESPONSE._serialized_start=41083 - _LISTHTLCSRESPONSE._serialized_end=41138 - _LISTHTLCSHTLCS._serialized_start=41141 - _LISTHTLCSHTLCS._serialized_end=41406 - _LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION._serialized_start=41364 - _LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION._serialized_end=41406 - _PINGREQUEST._serialized_start=41408 - _PINGREQUEST._serialized_end=41497 - _PINGRESPONSE._serialized_start=41499 - _PINGRESPONSE._serialized_end=41529 - _SENDCUSTOMMSGREQUEST._serialized_start=41531 - _SENDCUSTOMMSGREQUEST._serialized_end=41583 - _SENDCUSTOMMSGRESPONSE._serialized_start=41585 - _SENDCUSTOMMSGRESPONSE._serialized_end=41624 - _SETCHANNELREQUEST._serialized_start=41627 - _SETCHANNELREQUEST._serialized_end=41925 - _SETCHANNELRESPONSE._serialized_start=41927 - _SETCHANNELRESPONSE._serialized_end=41990 - _SETCHANNELCHANNELS._serialized_start=41993 - _SETCHANNELCHANNELS._serialized_end=42451 - _SIGNINVOICEREQUEST._serialized_start=42453 - _SIGNINVOICEREQUEST._serialized_end=42492 - _SIGNINVOICERESPONSE._serialized_start=42494 - _SIGNINVOICERESPONSE._serialized_end=42531 - _SIGNMESSAGEREQUEST._serialized_start=42533 - _SIGNMESSAGEREQUEST._serialized_end=42570 - _SIGNMESSAGERESPONSE._serialized_start=42572 - _SIGNMESSAGERESPONSE._serialized_end=42642 - _STOPREQUEST._serialized_start=42644 - _STOPREQUEST._serialized_end=42657 - _STOPRESPONSE._serialized_start=42659 - _STOPRESPONSE._serialized_end=42673 - _PREAPPROVEKEYSENDREQUEST._serialized_start=42676 - _PREAPPROVEKEYSENDREQUEST._serialized_end=42843 - _PREAPPROVEKEYSENDRESPONSE._serialized_start=42845 - _PREAPPROVEKEYSENDRESPONSE._serialized_end=42872 - _PREAPPROVEINVOICEREQUEST._serialized_start=42874 - _PREAPPROVEINVOICEREQUEST._serialized_end=42932 - _PREAPPROVEINVOICERESPONSE._serialized_start=42934 - _PREAPPROVEINVOICERESPONSE._serialized_end=42961 - _NODE._serialized_start=42964 - _NODE._serialized_end=46620 + _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_end=5316 + _LISTPEERSPEERSCHANNELSFUNDING._serialized_start=5319 + _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5602 + _LISTPEERSPEERSCHANNELSALIAS._serialized_start=5604 + _LISTPEERSPEERSCHANNELSALIAS._serialized_end=5695 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5698 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=6067 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5983 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=6038 + _LISTFUNDSREQUEST._serialized_start=6069 + _LISTFUNDSREQUEST._serialized_end=6117 + _LISTFUNDSRESPONSE._serialized_start=6119 + _LISTFUNDSRESPONSE._serialized_end=6220 + _LISTFUNDSOUTPUTS._serialized_start=6223 + _LISTFUNDSOUTPUTS._serialized_end=6610 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=6484 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=6565 + _LISTFUNDSCHANNELS._serialized_start=6613 + _LISTFUNDSCHANNELS._serialized_end=6912 + _SENDPAYREQUEST._serialized_start=6915 + _SENDPAYREQUEST._serialized_end=7264 + _SENDPAYRESPONSE._serialized_start=7267 + _SENDPAYRESPONSE._serialized_end=7860 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7681 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7723 + _SENDPAYROUTE._serialized_start=7862 + _SENDPAYROUTE._serialized_end=7954 + _LISTCHANNELSREQUEST._serialized_start=7957 + _LISTCHANNELSREQUEST._serialized_end=8104 + _LISTCHANNELSRESPONSE._serialized_start=8106 + _LISTCHANNELSRESPONSE._serialized_end=8173 + _LISTCHANNELSCHANNELS._serialized_start=8176 + _LISTCHANNELSCHANNELS._serialized_end=8611 + _ADDGOSSIPREQUEST._serialized_start=8613 + _ADDGOSSIPREQUEST._serialized_end=8648 + _ADDGOSSIPRESPONSE._serialized_start=8650 + _ADDGOSSIPRESPONSE._serialized_end=8669 + _AUTOCLEANINVOICEREQUEST._serialized_start=8671 + _AUTOCLEANINVOICEREQUEST._serialized_end=8782 + _AUTOCLEANINVOICERESPONSE._serialized_start=8785 + _AUTOCLEANINVOICERESPONSE._serialized_end=8914 + _CHECKMESSAGEREQUEST._serialized_start=8916 + _CHECKMESSAGEREQUEST._serialized_end=9001 + _CHECKMESSAGERESPONSE._serialized_start=9003 + _CHECKMESSAGERESPONSE._serialized_end=9059 + _CLOSEREQUEST._serialized_start=9062 + _CLOSEREQUEST._serialized_end=9393 + _CLOSERESPONSE._serialized_start=9396 + _CLOSERESPONSE._serialized_end=9567 + _CLOSERESPONSE_CLOSETYPE._serialized_start=9498 + _CLOSERESPONSE_CLOSETYPE._serialized_end=9551 + _CONNECTREQUEST._serialized_start=9569 + _CONNECTREQUEST._serialized_end=9653 + _CONNECTRESPONSE._serialized_start=9656 + _CONNECTRESPONSE._serialized_end=9836 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9801 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9836 + _CONNECTADDRESS._serialized_start=9839 + _CONNECTADDRESS._serialized_end=10090 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9978 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=10058 + _CREATEINVOICEREQUEST._serialized_start=10092 + _CREATEINVOICEREQUEST._serialized_end=10166 + _CREATEINVOICERESPONSE._serialized_start=10169 + _CREATEINVOICERESPONSE._serialized_end=10856 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10631 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10687 + _DATASTOREREQUEST._serialized_start=10859 + _DATASTOREREQUEST._serialized_end=11167 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=11012 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=11124 + _DATASTORERESPONSE._serialized_start=11170 + _DATASTORERESPONSE._serialized_end=11300 + _CREATEONIONREQUEST._serialized_start=11303 + _CREATEONIONREQUEST._serialized_end=11460 + _CREATEONIONRESPONSE._serialized_start=11462 + _CREATEONIONRESPONSE._serialized_end=11522 + _CREATEONIONHOPS._serialized_start=11524 + _CREATEONIONHOPS._serialized_end=11574 + _DELDATASTOREREQUEST._serialized_start=11576 + _DELDATASTOREREQUEST._serialized_end=11650 + _DELDATASTORERESPONSE._serialized_start=11653 + _DELDATASTORERESPONSE._serialized_end=11786 + _DELEXPIREDINVOICEREQUEST._serialized_start=11788 + _DELEXPIREDINVOICEREQUEST._serialized_end=11860 + _DELEXPIREDINVOICERESPONSE._serialized_start=11862 + _DELEXPIREDINVOICERESPONSE._serialized_end=11889 + _DELINVOICEREQUEST._serialized_start=11892 + _DELINVOICEREQUEST._serialized_end=12074 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=12008 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=12061 + _DELINVOICERESPONSE._serialized_start=12077 + _DELINVOICERESPONSE._serialized_end=12622 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=12008 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=12061 + _INVOICEREQUEST._serialized_start=12625 + _INVOICEREQUEST._serialized_end=12875 + _INVOICERESPONSE._serialized_start=12878 + _INVOICERESPONSE._serialized_end=13283 + _LISTDATASTOREREQUEST._serialized_start=13285 + _LISTDATASTOREREQUEST._serialized_end=13320 + _LISTDATASTORERESPONSE._serialized_start=13322 + _LISTDATASTORERESPONSE._serialized_end=13393 + _LISTDATASTOREDATASTORE._serialized_start=13396 + _LISTDATASTOREDATASTORE._serialized_end=13531 + _LISTINVOICESREQUEST._serialized_start=13534 + _LISTINVOICESREQUEST._serialized_end=13884 + _LISTINVOICESREQUEST_LISTINVOICESINDEX._serialized_start=13755 + _LISTINVOICESREQUEST_LISTINVOICESINDEX._serialized_end=13800 + _LISTINVOICESRESPONSE._serialized_start=13886 + _LISTINVOICESRESPONSE._serialized_end=13953 + _LISTINVOICESINVOICES._serialized_start=13956 + _LISTINVOICESINVOICES._serialized_end=14722 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=14456 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=14519 + _SENDONIONREQUEST._serialized_start=14725 + _SENDONIONREQUEST._serialized_end=15119 + _SENDONIONRESPONSE._serialized_start=15122 + _SENDONIONRESPONSE._serialized_end=15645 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=15493 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=15537 + _SENDONIONFIRST_HOP._serialized_start=15647 + _SENDONIONFIRST_HOP._serialized_end=15728 + _LISTSENDPAYSREQUEST._serialized_start=15731 + _LISTSENDPAYSREQUEST._serialized_end=15966 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=15868 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=15927 + _LISTSENDPAYSRESPONSE._serialized_start=15968 + _LISTSENDPAYSRESPONSE._serialized_end=16035 + _LISTSENDPAYSPAYMENTS._serialized_start=16038 + _LISTSENDPAYSPAYMENTS._serialized_end=16666 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=16472 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=16539 + _LISTTRANSACTIONSREQUEST._serialized_start=16668 + _LISTTRANSACTIONSREQUEST._serialized_end=16693 + _LISTTRANSACTIONSRESPONSE._serialized_start=16695 + _LISTTRANSACTIONSRESPONSE._serialized_end=16778 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=16781 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=17029 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=17031 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=17114 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=17116 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17224 + _PAYREQUEST._serialized_start=17227 + _PAYREQUEST._serialized_end=17701 + _PAYRESPONSE._serialized_start=17704 + _PAYRESPONSE._serialized_end=18083 + _PAYRESPONSE_PAYSTATUS._serialized_start=17986 + _PAYRESPONSE_PAYSTATUS._serialized_end=18036 + _LISTNODESREQUEST._serialized_start=18085 + _LISTNODESREQUEST._serialized_end=18127 + _LISTNODESRESPONSE._serialized_start=18129 + _LISTNODESRESPONSE._serialized_end=18184 + _LISTNODESNODES._serialized_start=18187 + _LISTNODESNODES._serialized_end=18412 + _LISTNODESNODESADDRESSES._serialized_start=18415 + _LISTNODESNODESADDRESSES._serialized_end=18647 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18555 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18635 + _WAITANYINVOICEREQUEST._serialized_start=18649 + _WAITANYINVOICEREQUEST._serialized_end=18752 + _WAITANYINVOICERESPONSE._serialized_start=18755 + _WAITANYINVOICERESPONSE._serialized_end=19378 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19187 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19232 + _WAITINVOICEREQUEST._serialized_start=19380 + _WAITINVOICEREQUEST._serialized_end=19415 + _WAITINVOICERESPONSE._serialized_start=19418 + _WAITINVOICERESPONSE._serialized_end=20029 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19841 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19883 + _WAITSENDPAYREQUEST._serialized_start=20032 + _WAITSENDPAYREQUEST._serialized_end=20174 + _WAITSENDPAYRESPONSE._serialized_start=20177 + _WAITSENDPAYRESPONSE._serialized_end=20739 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20581 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20614 + _NEWADDRREQUEST._serialized_start=20742 + _NEWADDRREQUEST._serialized_end=20893 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20826 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20877 + _NEWADDRRESPONSE._serialized_start=20895 + _NEWADDRRESPONSE._serialized_end=21014 + _WITHDRAWREQUEST._serialized_start=21017 + _WITHDRAWREQUEST._serialized_end=21219 + _WITHDRAWRESPONSE._serialized_start=21221 + _WITHDRAWRESPONSE._serialized_end=21279 + _KEYSENDREQUEST._serialized_start=21282 + _KEYSENDREQUEST._serialized_end=21668 + _KEYSENDRESPONSE._serialized_start=21671 + _KEYSENDRESPONSE._serialized_end=22041 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21965 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21994 + _FUNDPSBTREQUEST._serialized_start=22044 + _FUNDPSBTREQUEST._serialized_end=22464 + _FUNDPSBTRESPONSE._serialized_start=22467 + _FUNDPSBTRESPONSE._serialized_end=22684 + _FUNDPSBTRESERVATIONS._serialized_start=22686 + _FUNDPSBTRESERVATIONS._serialized_end=22803 + _SENDPSBTREQUEST._serialized_start=22805 + _SENDPSBTREQUEST._serialized_end=22870 + _SENDPSBTRESPONSE._serialized_start=22872 + _SENDPSBTRESPONSE._serialized_end=22916 + _SIGNPSBTREQUEST._serialized_start=22918 + _SIGNPSBTREQUEST._serialized_end=22967 + _SIGNPSBTRESPONSE._serialized_start=22969 + _SIGNPSBTRESPONSE._serialized_end=23008 + _UTXOPSBTREQUEST._serialized_start=23011 + _UTXOPSBTREQUEST._serialized_end=23422 + _UTXOPSBTRESPONSE._serialized_start=23425 + _UTXOPSBTRESPONSE._serialized_end=23642 + _UTXOPSBTRESERVATIONS._serialized_start=23644 + _UTXOPSBTRESERVATIONS._serialized_end=23761 + _TXDISCARDREQUEST._serialized_start=23763 + _TXDISCARDREQUEST._serialized_end=23795 + _TXDISCARDRESPONSE._serialized_start=23797 + _TXDISCARDRESPONSE._serialized_end=23851 + _TXPREPAREREQUEST._serialized_start=23854 + _TXPREPAREREQUEST._serialized_end=24018 + _TXPREPARERESPONSE._serialized_start=24020 + _TXPREPARERESPONSE._serialized_end=24088 + _TXSENDREQUEST._serialized_start=24090 + _TXSENDREQUEST._serialized_end=24119 + _TXSENDRESPONSE._serialized_start=24121 + _TXSENDRESPONSE._serialized_end=24177 + _LISTPEERCHANNELSREQUEST._serialized_start=24179 + _LISTPEERCHANNELSREQUEST._serialized_end=24228 + _LISTPEERCHANNELSRESPONSE._serialized_start=24230 + _LISTPEERCHANNELSRESPONSE._serialized_end=24305 + _LISTPEERCHANNELSCHANNELS._serialized_start=24308 + _LISTPEERCHANNELSCHANNELS._serialized_end=27535 + _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_start=26299 + _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_end=26620 + _LISTPEERCHANNELSCHANNELSFEERATE._serialized_start=27537 + _LISTPEERCHANNELSCHANNELSFEERATE._serialized_end=27630 + _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_start=27633 + _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_end=28017 + _LISTPEERCHANNELSCHANNELSFUNDING._serialized_start=28020 + _LISTPEERCHANNELSCHANNELSFUNDING._serialized_end=28358 + _LISTPEERCHANNELSCHANNELSALIAS._serialized_start=28360 + _LISTPEERCHANNELSCHANNELSALIAS._serialized_end=28453 + _LISTPEERCHANNELSCHANNELSHTLCS._serialized_start=28456 + _LISTPEERCHANNELSCHANNELSHTLCS._serialized_end=28938 + _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_start=28777 + _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_end=28834 + _LISTCLOSEDCHANNELSREQUEST._serialized_start=28940 + _LISTCLOSEDCHANNELSREQUEST._serialized_end=28991 + _LISTCLOSEDCHANNELSRESPONSE._serialized_start=28993 + _LISTCLOSEDCHANNELSRESPONSE._serialized_end=29084 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_start=29087 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_end=30289 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_start=29987 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_end=30105 + _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_start=30291 + _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_end=30392 + _DECODEPAYREQUEST._serialized_start=30394 + _DECODEPAYREQUEST._serialized_end=30470 + _DECODEPAYRESPONSE._serialized_start=30473 + _DECODEPAYRESPONSE._serialized_end=30998 + _DECODEPAYFALLBACKS._serialized_start=31001 + _DECODEPAYFALLBACKS._serialized_end=31199 + _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_start=31122 + _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_end=31190 + _DECODEPAYEXTRA._serialized_start=31201 + _DECODEPAYEXTRA._serialized_end=31244 + _DECODEREQUEST._serialized_start=31246 + _DECODEREQUEST._serialized_end=31277 + _DECODERESPONSE._serialized_start=31280 + _DECODERESPONSE._serialized_end=35546 + _DECODERESPONSE_DECODETYPE._serialized_start=33848 + _DECODERESPONSE_DECODETYPE._serialized_end=33956 + _DECODEOFFER_PATHS._serialized_start=35548 + _DECODEOFFER_PATHS._serialized_end=35608 + _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_start=35611 + _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_end=35749 + _DECODEINVOICE_PATHSPATH._serialized_start=35751 + _DECODEINVOICE_PATHSPATH._serialized_end=35835 + _DECODEINVOICE_FALLBACKS._serialized_start=35837 + _DECODEINVOICE_FALLBACKS._serialized_end=35926 + _DECODEFALLBACKS._serialized_start=35928 + _DECODEFALLBACKS._serialized_end=36047 + _DECODEEXTRA._serialized_start=36049 + _DECODEEXTRA._serialized_end=36089 + _DECODERESTRICTIONS._serialized_start=36091 + _DECODERESTRICTIONS._serialized_end=36150 + _DISCONNECTREQUEST._serialized_start=36152 + _DISCONNECTREQUEST._serialized_end=36213 + _DISCONNECTRESPONSE._serialized_start=36215 + _DISCONNECTRESPONSE._serialized_end=36235 + _FEERATESREQUEST._serialized_start=36237 + _FEERATESREQUEST._serialized_end=36344 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=36307 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=36344 + _FEERATESRESPONSE._serialized_start=36347 + _FEERATESRESPONSE._serialized_end=36631 + _FEERATESPERKB._serialized_start=36634 + _FEERATESPERKB._serialized_end=37101 + _FEERATESPERKBESTIMATES._serialized_start=37104 + _FEERATESPERKBESTIMATES._serialized_end=37254 + _FEERATESPERKW._serialized_start=37257 + _FEERATESPERKW._serialized_end=37724 + _FEERATESPERKWESTIMATES._serialized_start=37727 + _FEERATESPERKWESTIMATES._serialized_end=37877 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=37880 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=38163 + _FUNDCHANNELREQUEST._serialized_start=38166 + _FUNDCHANNELREQUEST._serialized_end=38651 + _FUNDCHANNELRESPONSE._serialized_start=38654 + _FUNDCHANNELRESPONSE._serialized_end=38809 + _GETROUTEREQUEST._serialized_start=38812 + _GETROUTEREQUEST._serialized_end=39048 + _GETROUTERESPONSE._serialized_start=39050 + _GETROUTERESPONSE._serialized_end=39103 + _GETROUTEROUTE._serialized_start=39106 + _GETROUTEROUTE._serialized_end=39303 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=39274 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=39303 + _LISTFORWARDSREQUEST._serialized_start=39306 + _LISTFORWARDSREQUEST._serialized_end=39564 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=39446 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=39522 + _LISTFORWARDSRESPONSE._serialized_start=39566 + _LISTFORWARDSRESPONSE._serialized_end=39633 + _LISTFORWARDSFORWARDS._serialized_start=39636 + _LISTFORWARDSFORWARDS._serialized_end=40242 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=40025 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=40109 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=40111 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=40159 + _LISTPAYSREQUEST._serialized_start=40245 + _LISTPAYSREQUEST._serialized_end=40464 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=40370 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=40425 + _LISTPAYSRESPONSE._serialized_start=40466 + _LISTPAYSRESPONSE._serialized_end=40517 + _LISTPAYSPAYS._serialized_start=40520 + _LISTPAYSPAYS._serialized_end=41159 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=40934 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=40993 + _LISTHTLCSREQUEST._serialized_start=41161 + _LISTHTLCSREQUEST._serialized_end=41203 + _LISTHTLCSRESPONSE._serialized_start=41205 + _LISTHTLCSRESPONSE._serialized_end=41260 + _LISTHTLCSHTLCS._serialized_start=41263 + _LISTHTLCSHTLCS._serialized_end=41528 + _LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION._serialized_start=41486 + _LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION._serialized_end=41528 + _PINGREQUEST._serialized_start=41530 + _PINGREQUEST._serialized_end=41619 + _PINGRESPONSE._serialized_start=41621 + _PINGRESPONSE._serialized_end=41651 + _SENDCUSTOMMSGREQUEST._serialized_start=41653 + _SENDCUSTOMMSGREQUEST._serialized_end=41705 + _SENDCUSTOMMSGRESPONSE._serialized_start=41707 + _SENDCUSTOMMSGRESPONSE._serialized_end=41746 + _SETCHANNELREQUEST._serialized_start=41749 + _SETCHANNELREQUEST._serialized_end=42047 + _SETCHANNELRESPONSE._serialized_start=42049 + _SETCHANNELRESPONSE._serialized_end=42112 + _SETCHANNELCHANNELS._serialized_start=42115 + _SETCHANNELCHANNELS._serialized_end=42573 + _SIGNINVOICEREQUEST._serialized_start=42575 + _SIGNINVOICEREQUEST._serialized_end=42614 + _SIGNINVOICERESPONSE._serialized_start=42616 + _SIGNINVOICERESPONSE._serialized_end=42653 + _SIGNMESSAGEREQUEST._serialized_start=42655 + _SIGNMESSAGEREQUEST._serialized_end=42692 + _SIGNMESSAGERESPONSE._serialized_start=42694 + _SIGNMESSAGERESPONSE._serialized_end=42764 + _STOPREQUEST._serialized_start=42766 + _STOPREQUEST._serialized_end=42779 + _STOPRESPONSE._serialized_start=42781 + _STOPRESPONSE._serialized_end=42795 + _PREAPPROVEKEYSENDREQUEST._serialized_start=42798 + _PREAPPROVEKEYSENDREQUEST._serialized_end=42965 + _PREAPPROVEKEYSENDRESPONSE._serialized_start=42967 + _PREAPPROVEKEYSENDRESPONSE._serialized_end=42994 + _PREAPPROVEINVOICEREQUEST._serialized_start=42996 + _PREAPPROVEINVOICEREQUEST._serialized_end=43054 + _PREAPPROVEINVOICERESPONSE._serialized_start=43056 + _PREAPPROVEINVOICERESPONSE._serialized_end=43083 + _NODE._serialized_start=43086 + _NODE._serialized_end=46742 # @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-spec/Makefile b/contrib/pyln-spec/Makefile index 14ad23aa7f0b..e497bb580710 100755 --- a/contrib/pyln-spec/Makefile +++ b/contrib/pyln-spec/Makefile @@ -100,5 +100,5 @@ $(CODE_DIRS:%=%/csv.py): $(CODE_DIRS:%=%/text.py): @echo 'desc = "'`head -n1 $< | cut -c3-`'"' > $@.tmp - @(echo -n 'text = """'; sed 's,\\,\\\\,g' < $<; echo '"""') >> $@.tmp + @(printf '%s' 'text = """'; sed 's,\\,\\\\,g' < $<; echo '"""') >> $@.tmp @if cmp $@ $@.tmp >/dev/null 2>&1; then rm $@.tmp; echo '$@ unchanged'; else mv $@.tmp $@; fi diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index 619cb9c636f5..7c7788dd09af 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -91,6 +91,7 @@ def listpeers_peers_channels_inflight2py(m): "feerate": m.feerate, # PrimitiveField in generate_composite "total_funding_msat": amount2msat(m.total_funding_msat), # PrimitiveField in generate_composite "our_funding_msat": amount2msat(m.our_funding_msat), # PrimitiveField in generate_composite + "splice_amount": m.splice_amount, # PrimitiveField in generate_composite "scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite }) @@ -749,6 +750,7 @@ def listpeerchannels_channels_inflight2py(m): "funding_outnum": m.funding_outnum, # PrimitiveField in generate_composite "feerate": m.feerate, # PrimitiveField in generate_composite "total_funding_msat": amount2msat(m.total_funding_msat), # PrimitiveField in generate_composite + "splice_amount": m.splice_amount, # PrimitiveField in generate_composite "our_funding_msat": amount2msat(m.our_funding_msat), # PrimitiveField in generate_composite "scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite }) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index b95978736c16..e42ef9cc599c 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -80,6 +80,7 @@ def env(name, default=None): DEPRECATED_APIS = env("DEPRECATED_APIS", "0") == "1" TIMEOUT = int(env("TIMEOUT", 180 if SLOW_MACHINE else 60)) EXPERIMENTAL_DUAL_FUND = env("EXPERIMENTAL_DUAL_FUND", "0") == "1" +EXPERIMENTAL_SPLICING = env("EXPERIMENTAL_SPLICING", "0") == "1" def wait_for(success, timeout=TIMEOUT): @@ -787,6 +788,8 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai self.daemon.opts["dev-no-reconnect"] = None if EXPERIMENTAL_DUAL_FUND: self.daemon.opts["experimental-dual-fund"] = None + if EXPERIMENTAL_SPLICING: + self.daemon.opts["experimental-splicing"] = None if options is not None: self.daemon.opts.update(options) diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index 07279eb8a371..0cc348668242 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -100,6 +100,7 @@ start_nodes() { dev-fast-gossip dev-bitcoind-poll=5 experimental-dual-fund + experimental-splicing experimental-offers funder-policy=match funder-policy-mod=100 @@ -114,7 +115,7 @@ start_nodes() { # Start the lightning nodes test -f "/tmp/l$i-$network/lightningd-$network.pid" || \ - $EATMYDATA "$LIGHTNINGD" "--network=$network" "--lightning-dir=/tmp/l$i-$network" "--bitcoin-datadir=$PATH_TO_BITCOIN" & + $EATMYDATA "$LIGHTNINGD" "--network=$network" "--lightning-dir=/tmp/l$i-$network" "--bitcoin-datadir=$PATH_TO_BITCOIN" "--database-upgrade=true" & # shellcheck disable=SC2139 disable=SC2086 alias l$i-cli="$LCLI --lightning-dir=/tmp/l$i-$network" # shellcheck disable=SC2139 disable=SC2086 diff --git a/db/bindings.c b/db/bindings.c index aa9795161a56..7a0951e31760 100644 --- a/db/bindings.c +++ b/db/bindings.c @@ -81,6 +81,12 @@ void db_bind_u64(struct db_stmt *stmt, u64 val) stmt->bindings[pos].v.u64 = val; } +void db_bind_s64(struct db_stmt *stmt, s64 val) +{ + u64 uval = val; + db_bind_u64(stmt, uval); +} + void db_bind_blob(struct db_stmt *stmt, const u8 *val, size_t len) { size_t pos = check_bind_pos(stmt); @@ -277,6 +283,11 @@ u64 db_col_u64(struct db_stmt *stmt, const char *colname) return stmt->db->config->column_u64_fn(stmt, col); } +u64 db_col_s64(struct db_stmt *stmt, const char *colname) +{ + return db_col_u64(stmt, colname); +} + int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def) { size_t col = db_query_colnum(stmt, colname); diff --git a/db/bindings.h b/db/bindings.h index 968889687e3e..f29fde99cab3 100644 --- a/db/bindings.h +++ b/db/bindings.h @@ -21,6 +21,7 @@ struct wally_tx; void db_bind_null(struct db_stmt *stmt); void db_bind_int(struct db_stmt *stmt, int val); void db_bind_u64(struct db_stmt *stmt, u64 val); +void db_bind_s64(struct db_stmt *stmt, s64 val); void db_bind_blob(struct db_stmt *stmt, const u8 *val, size_t len); void db_bind_text(struct db_stmt *stmt, const char *val); void db_bind_preimage(struct db_stmt *stmt, const struct preimage *p); @@ -62,6 +63,7 @@ size_t db_query_colnum(const struct db_stmt *stmt, const char *colname); int db_col_is_null(struct db_stmt *stmt, const char *colname); int db_col_int(struct db_stmt *stmt, const char *colname); u64 db_col_u64(struct db_stmt *stmt, const char *colname); +u64 db_col_s64(struct db_stmt *stmt, const char *colname); size_t db_col_bytes(struct db_stmt *stmt, const char *colname); const void* db_col_blob(struct db_stmt *stmt, const char *colname); char *db_col_strdup(const tal_t *ctx, diff --git a/doc/contribute-to-core-lightning/testing.md b/doc/contribute-to-core-lightning/testing.md index bfc6fd5443e5..cadec0235e38 100644 --- a/doc/contribute-to-core-lightning/testing.md +++ b/doc/contribute-to-core-lightning/testing.md @@ -59,7 +59,8 @@ TEST_CHECK_DBSTMTS=[0|1] - When running blackbox tests, this will Note: Only SQLite3. TEST_DB_PROVIDER=[sqlite3|postgres] - Selects the database to use when running blackbox tests. -EXPERIMENTAL_DUAL_FUND=[0|1] - Enable dual-funding tests. +EXPERIMENTAL_DUAL_FUND=[0|1] - Enable dual-funding tests. +EXPERIMENTAL_SPLICING=[0|1] - Enable splicing tests. ``` #### Troubleshooting diff --git a/doc/index.rst b/doc/index.rst index 9adb7b0636ac..a0eeee988913 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -131,6 +131,7 @@ Core Lightning Documentation lightning-signinvoice lightning-signmessage lightning-signpsbt + lightning-splice_init lightning-sql lightning-staticbackup lightning-stop diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index cd315a46fd67..79c0d2d593f9 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -120,6 +120,9 @@ On success, an object is returned, containing: - **experimental-dual-fund** (object, optional): - **set** (boolean): `true` if set in config or cmdline - **source** (string): source of configuration setting + - **experimental-splicing** (object, optional) *(added v23.08)*: + - **set** (boolean): `true` if set in config or cmdline + - **source** (string): source of configuration setting - **experimental-onion-messages** (object, optional): - **set** (boolean): `true` if set in config or cmdline - **source** (string): source of configuration setting @@ -298,6 +301,7 @@ On success, an object is returned, containing: - **wallet** (string, optional): `wallet` field from config or cmdline default **deprecated, removal in v24.05** - **large-channels** (boolean, optional): `large-channels` field from config or cmdline, or default **deprecated, removal in v24.05** - **experimental-dual-fund** (boolean, optional): `experimental-dual-fund` field from config or cmdline, or default **deprecated, removal in v24.05** +- **experimental-splicing** (boolean, optional): `experimental-splicing` field from config or cmdline, or default **deprecated, removal in v24.05** - **experimental-onion-messages** (boolean, optional): `experimental-onion-messages` field from config or cmdline, or default **deprecated, removal in v24.05** - **experimental-offers** (boolean, optional): `experimental-offers` field from config or cmdline, or default **deprecated, removal in v24.05** - **experimental-shutdown-wrong-funding** (boolean, optional): `experimental-shutdown-wrong-funding` field from config or cmdline, or default **deprecated, removal in v24.05** @@ -467,4 +471,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a40882cad0d889aa736a2932250102be43ae7e62b3d2429b26e0961e4c315f7b) +[comment]: # ( SHA256STAMP:8e7ec36b820cb17ecfc3066802bb07e159fffdd8dfe049d092b8f3b804e05588) diff --git a/doc/lightning-listpeerchannels.7.md b/doc/lightning-listpeerchannels.7.md index a97e678e0e0d..426e7ad6cfa0 100644 --- a/doc/lightning-listpeerchannels.7.md +++ b/doc/lightning-listpeerchannels.7.md @@ -26,7 +26,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **peer\_id** (pubkey): Node Public key - **peer\_connected** (boolean): A boolean flag that is set to true if the peer is online -- **state** (string): the channel state, in particular "CHANNELD\_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN") +- **state** (string): the channel state, in particular "CHANNELD\_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN", "CHANNELD\_AWAITING\_SPLICE") - **opener** (string): Who initiated the channel (one of "local", "remote") - **features** (array of strings): - BOLT #9 features which apply to this channel (one of "option\_static\_remotekey", "option\_anchor\_outputs", "option\_anchors\_zero\_fee\_htlc\_tx", "option\_scid\_alias", "option\_zeroconf") @@ -49,11 +49,12 @@ On success, an object containing **channels** is returned. It is an array of ob - **last\_feerate** (string, optional): For inflight opens, the most recent feerate used on the channel open - **next\_feerate** (string, optional): For inflight opens, the next feerate we'll use for the channel open - **next\_fee\_step** (u32, optional): For inflight opens, the next feerate step we'll use for the channel open -- **inflight** (array of objects, optional): Current candidate funding transactions (only for dual-funding): +- **inflight** (array of objects, optional): Current candidate funding transactions: - **funding\_txid** (txid): ID of the funding transaction - **funding\_outnum** (u32): The 0-based output number of the funding transaction which opens the channel - **feerate** (string): The feerate for this funding transaction in per-1000-weight, with "kpw" appended - **total\_funding\_msat** (msat): total amount in the channel + - **splice\_amount** (integer): The amouont of sats we're splicing in or out *(added v23.08)* - **our\_funding\_msat** (msat): amount we have in the channel - **scratch\_txid** (txid): The commitment transaction txid we would use if we went onchain now - **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close @@ -195,4 +196,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:c530e39c9144b5fbef0f6474170865095bd7b4c48e6378fdb7f9a7697b909473) +[comment]: # ( SHA256STAMP:dade32248bd309f2514a237cb71be6ddbe12220e1b6899693a032b45b7980a01) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 21462a787d5e..3b95a1688068 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -89,6 +89,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **feerate** (string): The feerate for this funding transaction in per-1000-weight, with "kpw" appended - **total\_funding\_msat** (msat): total amount in the channel - **our\_funding\_msat** (msat): amount we have in the channel + - **splice\_amount** (integer): The amouont of sats we're splicing in or out *(added v23.08)* - **scratch\_txid** (txid): The commitment transaction txid we would use if we went onchain now - **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close - **private** (boolean, optional): if False, we will not announce this channel @@ -398,4 +399,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:c0d0cc8f083168fd76caa2430a7c7d27d72a5273c55fb14b0efcbcb7a87274f4) +[comment]: # ( SHA256STAMP:d75b5070288f26a39df39831212f40c397f1389e7c1765f22829d3f3389a56aa) diff --git a/doc/lightning-splice_init.7.md b/doc/lightning-splice_init.7.md new file mode 100644 index 000000000000..291b4ea9e71b --- /dev/null +++ b/doc/lightning-splice_init.7.md @@ -0,0 +1,82 @@ +lightning-splice\_init -- Command to initiate a channel to a peer +===================================================================== + +SYNOPSIS +-------- +**(WARNING: experimental-splicing only)** + +**splice\_init** *channel\_id* *relative\_amount* [*initalpsbt*] [*feerate\_per\_kw*] [*force\_feerate*] + +DESCRIPTION +----------- + +`splice_init` is a low level RPC command which initiates a channel splice for a +given channel specified by `channel_id`. + +*channel\_id* is the channel id of the channel to be spliced. + +*relative\_amount* is a positive or negative amount of satoshis to add or +subtract from the channel. + +*initalpsbt* is the (optional) base 64 encoded PSBT to begin with. If not +specified, one will be generated automatically. + +*feerate\_per\_kw* is the miner fee we promise our peer to pay for our side of +the splice transaction. It is calculated by `feerate_per_kw` * +our\_bytes\_in\_splice\_tx / 1000. + +*force\_feerate* is a boolean flag. By default splices will fail if the fee +provided looks too high. This is to protect against accidentally setting your +fee higher than intended. Set `force_feerate` to true to skip this saftey check. + +Here is an example set of splice commands that will splice in 100,000 sats to +the first channel that comes out of `listpeerchannels`. The example assumes +you already have at least one confirmed channel. +```shell +RESULT=$(lightning-cli listpeerchannels) +CHANNEL_ID=$(echo $RESULT| jq -r ".channels[0].channel_id") +echo $RESULT + +RESULT=$(lightning-cli fundpsbt -k satoshi=100000sat feerate=urgent startweight=800 excess_as_change=true) +INITIALPSBT=$(echo $RESULT | jq -r ".psbt") +echo $RESULT + +RESULT=$(lightning-cli splice_init $CHANNEL_ID 100000 $INITIALPSBT) +PSBT=$(echo $RESULT | jq -r ".psbt") +echo $RESULT + +RESULT=$(lightning-cli splice_update $CHANNEL_ID $PSBT) +PSBT=$(echo $RESULT | jq -r ".psbt") +echo $RESULT + +RESULT=$(lightning-cli signpsbt $PSBT) +PSBT=$(echo $RESULT | jq -r ".signed_psbt") +echo $RESULT + +lightning-cli splice_signed $CHANNEL_ID $PSBT +``` + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: + +- **psbt** (string): the (incomplete) PSBT of the splice transaction + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +SEE ALSO +-------- + +AUTHOR +------ + +@dusty\_daemon + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:40121e2e7b0db8c99de12b4fd086f58f63e0d6643b9da1c1697a34dd5057454e) diff --git a/doc/lightning-sql.7.md b/doc/lightning-sql.7.md index 7937e5c328d2..d24e7fffb38d 100644 --- a/doc/lightning-sql.7.md +++ b/doc/lightning-sql.7.md @@ -271,6 +271,7 @@ The following tables are currently supported: - `funding_outnum` (type `u32`, sqltype `INTEGER`) - `feerate` (type `string`, sqltype `TEXT`) - `total_funding_msat` (type `msat`, sqltype `INTEGER`) + - `splice_amount` (type `integer`, sqltype `INTEGER`) - `our_funding_msat` (type `msat`, sqltype `INTEGER`) - `scratch_txid` (type `txid`, sqltype `BLOB`) - `close_to` (type `hex`, sqltype `BLOB`) @@ -513,4 +514,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:094d94f4d2cfd8e8bdc99be8b692100d3cbd70ab3c297ed8e191c8d4a0f9b6a7) +[comment]: # ( SHA256STAMP:2f77078555f16a9dbee5f068d4d0ba18727aeb378be674cd96bf7c1554a74ce5) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 4488ff5ccf10..50ac7315b826 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -712,6 +712,16 @@ about whether to add funds or not to a proposed channel is handled automatically by a plugin that implements the appropriate logic for your needs. The default behavior is to not contribute funds. +* **experimental-splicing** + + Specifying this enables support for the splicing protocol ([bolt][bolt] #863), +allowing both parties to dynamically adjust the size a channel. These changes +can be built interactively using PSBT and combined with other channel actions +including dual fund, additional channel splices, or generic transaction activity. +The operations will be bundled into a single transaction. The channel will remain +active while awaiting splice confirmation, however you can only spend the smaller +of the prior channel balance and the new one. + * **experimental-websocket-port**=*PORT* Specifying this enables support for accepting incoming WebSocket diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index 9e92409ca52f..d2a639b5fba3 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -402,6 +402,25 @@ } } }, + "experimental-splicing": { + "added": "v23.08", + "type": "object", + "additionalProperties": false, + "required": [ + "set", + "source" + ], + "properties": { + "set": { + "type": "boolean", + "description": "`true` if set in config or cmdline" + }, + "source": { + "type": "string", + "description": "source of configuration setting" + } + } + }, "experimental-onion-messages": { "type": "object", "additionalProperties": false, @@ -1449,6 +1468,11 @@ "type": "boolean", "description": "`experimental-dual-fund` field from config or cmdline, or default" }, + "experimental-splicing": { + "deprecated": "v23.08", + "type": "boolean", + "description": "`experimental-splicing` field from config or cmdline, or default" + }, "experimental-onion-messages": { "deprecated": "v23.08", "type": "boolean", diff --git a/doc/schemas/listpeerchannels.schema.json b/doc/schemas/listpeerchannels.schema.json index 730b4c84ea08..3d994c08cb8b 100644 --- a/doc/schemas/listpeerchannels.schema.json +++ b/doc/schemas/listpeerchannels.schema.json @@ -41,7 +41,8 @@ "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", - "DUALOPEND_AWAITING_LOCKIN" + "DUALOPEND_AWAITING_LOCKIN", + "CHANNELD_AWAITING_SPLICE" ], "description": "the channel state, in particular \"CHANNELD_NORMAL\" means the channel can be used normally" }, @@ -146,7 +147,7 @@ }, "inflight": { "type": "array", - "description": "Current candidate funding transactions (only for dual-funding)", + "description": "Current candidate funding transactions", "items": { "type": "object", "additionalProperties": false, @@ -155,6 +156,7 @@ "funding_outnum", "feerate", "total_funding_msat", + "splice_amount", "our_funding_msat", "scratch_txid" ], @@ -175,6 +177,11 @@ "type": "msat", "description": "total amount in the channel" }, + "splice_amount": { + "type": "integer", + "added": "v23.08", + "description": "The amouont of sats we're splicing in or out" + }, "our_funding_msat": { "type": "msat", "description": "amount we have in the channel" diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index d465b955c92b..efe4c8f5227e 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -273,6 +273,7 @@ "feerate", "total_funding_msat", "our_funding_msat", + "splice_amount", "scratch_txid" ], "properties": { @@ -296,6 +297,11 @@ "type": "msat", "description": "amount we have in the channel" }, + "splice_amount": { + "type": "integer", + "added": "v23.08", + "description": "The amouont of sats we're splicing in or out" + }, "scratch_txid": { "type": "txid", "description": "The commitment transaction txid we would use if we went onchain now" diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index f23d96a91ef4..f50750deb7f8 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -534,7 +534,7 @@ static u8 *create_unsigned_update(const tal_t *ctx, /* So valgrind doesn't complain */ memset(&dummy_sig, 0, sizeof(dummy_sig)); - /* BOLT #7: + /* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #7: * * The `channel_flags` bitfield is used to indicate the direction of * the channel: it identifies the node that this update originated @@ -760,7 +760,7 @@ void refresh_local_channel(struct daemon *daemon, return; } - /* BOLT #7: + /* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #7: * * The `channel_flags` bitfield is used to indicate the direction of * the channel: it identifies the node that this update originated diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 88c78c8b8c27..c0b708442064 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -641,6 +641,9 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: break; } diff --git a/gossipd/routing.c b/gossipd/routing.c index 244d54a0d04c..740be6d9857e 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -505,7 +505,7 @@ static void remove_chan_from_node(struct routing_state *rstate, /* Last channel? Simply delete node (and associated announce) */ if (num_chans == 0) { - if(node->rgraph.index != node->bcast.index) + if (node->rgraph.index != node->bcast.index) gossip_store_delete(rstate->gs, &node->rgraph, WIRE_NODE_ANNOUNCEMENT); @@ -522,7 +522,7 @@ static void remove_chan_from_node(struct routing_state *rstate, /* Removed only public channel? Remove node announcement. */ if (!node_has_broadcastable_channels(node)) { - if(node->rgraph.index != node->bcast.index) + if (node->rgraph.index != node->bcast.index) gossip_store_delete(rstate->gs, &node->rgraph, WIRE_NODE_ANNOUNCEMENT); diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index f94040aec257..73119731d8e5 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -661,6 +661,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX: case WIRE_HSMD_SIGN_REMOTE_HTLC_TX: case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX: + case WIRE_HSMD_SIGN_SPLICE_TX: case WIRE_HSMD_GET_PER_COMMITMENT_POINT: case WIRE_HSMD_SIGN_WITHDRAWAL: case WIRE_HSMD_GET_CHANNEL_BASEPOINTS: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 2c7afecb92f2..66179b3c9f27 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -248,6 +248,12 @@ msgtype,hsmd_sign_mutual_close_tx,21 msgdata,hsmd_sign_mutual_close_tx,tx,bitcoin_tx, msgdata,hsmd_sign_mutual_close_tx,remote_funding_key,pubkey, +# channeld asks HSM to sign splice tx. +msgtype,hsmd_sign_splice_tx,29 +msgdata,hsmd_sign_splice_tx,tx,bitcoin_tx, +msgdata,hsmd_sign_splice_tx,remote_funding_key,pubkey, +msgdata,hsmd_sign_splice_tx,input_index,u32, + # Reply for all the above requests. msgtype,hsmd_sign_tx_reply,112 msgdata,hsmd_sign_tx_reply,sig,bitcoin_signature, diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 887136000335..b157f4f250c3 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -106,6 +106,9 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX: return (client->capabilities & HSM_CAP_SIGN_CLOSING_TX) != 0; + case WIRE_HSMD_SIGN_SPLICE_TX: + return (client->capabilities & WIRE_HSMD_SIGN_SPLICE_TX) != 0; + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER: return (client->capabilities & HSM_CAP_SIGN_WILL_FUND_OFFER) != 0; @@ -1162,6 +1165,40 @@ static u8 *handle_sign_mutual_close_tx(struct hsmd_client *c, const u8 *msg_in) return towire_hsmd_sign_tx_reply(NULL, &sig); } +/* This is used by channeld to sign the final splice tx. */ +static u8 *handle_sign_splice_tx(struct hsmd_client *c, const u8 *msg_in) +{ + struct secret channel_seed; + struct bitcoin_tx *tx; + struct pubkey remote_funding_pubkey, local_funding_pubkey; + struct bitcoin_signature sig; + struct secrets secrets; + unsigned int input_index; + const u8 *funding_wscript; + + if (!fromwire_hsmd_sign_splice_tx(tmpctx, msg_in, + &tx, + &remote_funding_pubkey, + &input_index)) + return hsmd_status_malformed_request(c, msg_in); + + tx->chainparams = c->chainparams; + get_channel_seed(&c->id, c->dbid, &channel_seed); + derive_basepoints(&channel_seed, + &local_funding_pubkey, NULL, &secrets, NULL); + + funding_wscript = bitcoin_redeem_2of2(tmpctx, + &local_funding_pubkey, + &remote_funding_pubkey); + + sign_tx_input(tx, input_index, NULL, funding_wscript, + &secrets.funding_privkey, + &local_funding_pubkey, + SIGHASH_ALL, &sig); + + return towire_hsmd_sign_tx_reply(NULL, &sig); +} + /*~ Originally, onchaind would ask for hsmd to sign txs directly, and then * tell lightningd to broadcast it. With "bring-your-own-fees" HTLCs, this * changed, since we need to find a UTXO to attach to the transaction, @@ -1901,6 +1938,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_sign_withdrawal_tx(client, msg); case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX: return handle_sign_mutual_close_tx(client, msg); + case WIRE_HSMD_SIGN_SPLICE_TX: + return handle_sign_splice_tx(client, msg); case WIRE_HSMD_SIGN_LOCAL_HTLC_TX: return handle_sign_local_htlc_tx(client, msg); case WIRE_HSMD_SIGN_REMOTE_HTLC_TX: @@ -1985,6 +2024,7 @@ u8 *hsmd_init(struct secret hsm_secret, WIRE_HSMD_SIGN_ANY_DELAYED_PAYMENT_TO_US, WIRE_HSMD_SIGN_ANCHORSPEND, WIRE_HSMD_SIGN_HTLC_TX_MINGLE, + WIRE_HSMD_SIGN_SPLICE_TX, }; /*~ Don't swap this. */ diff --git a/lightningd/channel.c b/lightningd/channel.c index 73a9430f27a7..d830aca527bc 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -133,7 +133,9 @@ new_inflight(struct channel *channel, const u32 lease_chan_max_msat, const u16 lease_chan_max_ppt, const u32 lease_blockheight_start, const struct amount_msat lease_fee, - const struct amount_sat lease_amt) + const struct amount_sat lease_amt, + s64 splice_amnt, + bool i_am_initiator) { struct wally_psbt *last_tx_psbt_clone; struct channel_inflight *inflight @@ -145,6 +147,7 @@ new_inflight(struct channel *channel, funding->total_funds = total_funds; funding->feerate = funding_feerate; funding->our_funds = our_funds; + funding->splice_amnt = splice_amnt; inflight->funding = funding; inflight->channel = channel; @@ -152,8 +155,11 @@ new_inflight(struct channel *channel, inflight->funding_psbt = tal_steal(inflight, psbt); /* Make a 'clone' of this tx */ - last_tx_psbt_clone = clone_psbt(inflight, last_tx->psbt); - inflight->last_tx = bitcoin_tx_with_psbt(inflight, last_tx_psbt_clone); + inflight->last_tx = NULL; + if (last_tx) { + last_tx_psbt_clone = clone_psbt(inflight, last_tx->psbt); + inflight->last_tx = bitcoin_tx_with_psbt(inflight, last_tx_psbt_clone); + } inflight->last_sig = last_sig; inflight->tx_broadcast = false; @@ -169,6 +175,8 @@ new_inflight(struct channel *channel, inflight->lease_fee = lease_fee; inflight->lease_amt = lease_amt; + inflight->i_am_initiator = i_am_initiator; + list_add_tail(&channel->inflights, &inflight->list); tal_add_destructor(inflight, destroy_inflight); @@ -580,6 +588,23 @@ const char *channel_state_str(enum channel_state state) return "unknown"; } +bool channel_state_normalish(const struct channel *channel) +{ + return channel->state == CHANNELD_NORMAL + || channel->state == CHANNELD_AWAITING_SPLICE; +} + +bool channel_state_awaitish(const struct channel *channel) +{ + return channel->state == CHANNELD_AWAITING_LOCKIN + || channel->state == CHANNELD_AWAITING_SPLICE; +} + +bool channel_state_closish(enum channel_state channel_state) +{ + return channel_state > CHANNELD_NORMAL && channel_state <= CLOSED; +} + struct channel *peer_any_active_channel(struct peer *peer, bool *others) { struct channel *channel, *ret = NULL; @@ -793,7 +818,7 @@ void channel_set_state(struct channel *channel, struct timeabs timestamp; /* set closer, if known */ - if (state > CHANNELD_NORMAL && channel->closer == NUM_SIDES) { + if (channel_state_closish(state) && channel->closer == NUM_SIDES) { if (reason == REASON_LOCAL) channel->closer = LOCAL; if (reason == REASON_USER) channel->closer = LOCAL; if (reason == REASON_REMOTE) channel->closer = REMOTE; diff --git a/lightningd/channel.h b/lightningd/channel.h index a34de93e042e..cb3d6eeb9249 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -25,6 +25,9 @@ struct funding_info { /* Our original funds, in funding amount */ struct amount_sat our_funds; + + /* Relative splicing balance change */ + s64 splice_amnt; }; struct channel_inflight { @@ -57,6 +60,9 @@ struct channel_inflight { /* Amount requested to lease for this open */ struct amount_sat lease_amt; + + /* Did I initate this splice attempt? */ + bool i_am_initiator; }; struct open_attempt { @@ -366,7 +372,9 @@ struct channel_inflight *new_inflight(struct channel *channel, const u16 lease_chan_max_ppt, const u32 lease_blockheight_start, const struct amount_msat lease_fee, - const struct amount_sat lease_amt); + const struct amount_sat lease_amt, + s64 splice_amnt, + bool i_am_initiator); /* Given a txid, find an inflight channel stub. Returns NULL if none found */ struct channel_inflight *channel_inflight_find(struct channel *channel, @@ -384,6 +392,15 @@ void delete_channel(struct channel *channel STEALS); const char *channel_state_name(const struct channel *channel); const char *channel_state_str(enum channel_state state); +/* Is the channel in NORMAL or AWAITING_SPLICE state? */ +bool channel_state_normalish(const struct channel *channel); + +/* Is the channel in AWAITING_*? */ +bool channel_state_awaitish(const struct channel *channel); + +/* Is the channel in one of the CLOSING or CLOSED like states? */ +bool channel_state_closish(enum channel_state channel_state); + void channel_set_owner(struct channel *channel, struct subd *owner); /* Channel has failed, but can try again. */ @@ -454,12 +471,12 @@ void channel_set_last_tx(struct channel *channel, static inline bool channel_can_add_htlc(const struct channel *channel) { - return channel->state == CHANNELD_NORMAL; + return channel_state_normalish(channel); } static inline bool channel_fees_can_change(const struct channel *channel) { - return channel->state == CHANNELD_NORMAL + return channel_state_normalish(channel) || channel->state == CHANNELD_SHUTTING_DOWN; } diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 21bf7b7a97ea..f3c6bc633c9b 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1,5 +1,7 @@ #include "config.h" #include +#include +#include #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +26,16 @@ #include #include #include +#include + +struct splice_command { + /* Inside struct lightningd splice_commands. */ + struct list_node list; + /* Command structure. This is the parent of the splice command. */ + struct command *cmd; + /* Channel being spliced. */ + struct channel *channel; +}; void channel_update_feerates(struct lightningd *ld, const struct channel *channel) { @@ -148,6 +161,515 @@ void notify_feerate_change(struct lightningd *ld) * peer. We *could* do so, however. */ } +static struct splice_command *splice_command_for_chan(struct lightningd *ld, + struct channel *channel) +{ + struct splice_command *cc; + + list_for_each(&ld->splice_commands, cc, list) + if (channel == cc->channel) + return cc; + + return NULL; +} + +static void handle_splice_funding_error(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct splice_command *cc; + struct amount_msat funding, req_funding; + bool opener_error; + + if (!fromwire_channeld_splice_funding_error(msg, &funding, + &req_funding, + &opener_error)) { + channel_internal_error(channel, + "bad channeld_splice_feerate_error %s", + tal_hex(channel, msg)); + return; + } + + cc = splice_command_for_chan(ld, channel); + if (cc) { + struct json_stream *response = json_stream_success(cc->cmd); + json_add_string(response, "message", "Splice funding too low"); + json_add_string(response, "error", + tal_fmt(tmpctx, + "%s provided %s but committed to %s.", + opener_error ? "You" : "Peer", + fmt_amount_msat(tmpctx, funding), + fmt_amount_msat(tmpctx, req_funding))); + + was_pending(command_success(cc->cmd, response)); + } + else + log_peer_unusual(ld->log, &channel->peer->id, + "Splice funding too low. %s provided but %s" + " commited to %s", + opener_error ? "peer" : "you", + fmt_amount_msat(tmpctx, funding), + fmt_amount_msat(tmpctx, req_funding)); +} + +static void handle_splice_state_error(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct splice_command *cc; + char *error_msg; + + if (!fromwire_channeld_splice_state_error(tmpctx, msg, &error_msg)) { + channel_internal_error(channel, + "bad channeld_splice_state_error %s", + tal_hex(channel, msg)); + return; + } + + cc = splice_command_for_chan(ld, channel); + if (cc) { + struct json_stream *response = json_stream_success(cc->cmd); + json_add_string(response, "message", "Splice state error"); + json_add_string(response, "error", error_msg); + + was_pending(command_success(cc->cmd, response)); + } + else + log_peer_unusual(ld->log, &channel->peer->id, + "Splice state error: %s", error_msg); +} + +static void handle_splice_feerate_error(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct splice_command *cc; + struct amount_msat fee; + bool too_high; + + if (!fromwire_channeld_splice_feerate_error(msg, &fee, &too_high)) { + channel_internal_error(channel, + "bad fromwire_channeld_splice_feerate_error %s", + tal_hex(channel, msg)); + return; + } + + cc = splice_command_for_chan(ld, channel); + if (cc) { + struct json_stream *response = json_stream_success(cc->cmd); + json_add_string(response, "message", "Splice feerate failed"); + + if (too_high) + json_add_string(response, "error", + tal_fmt(tmpctx, "Feerate too high. Do you " + "really want to spend %s on fees?", + fmt_amount_msat(tmpctx, fee))); + else + json_add_string(response, "error", + tal_fmt(tmpctx, "Feerate too low. Your " + "funding only provided %s in fees", + fmt_amount_msat(tmpctx, fee))); + + was_pending(command_success(cc->cmd, response)); + } + else + log_peer_unusual(ld->log, &channel->peer->id, "Peer gave us a" + " splice pkg with too low of feerate (fee was" + " %s), we rejected it.", + fmt_amount_msat(tmpctx, fee)); +} + +/* When channeld finishes processing the `splice_init` command, this is called */ +static void handle_splice_confirmed_init(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct splice_command *cc; + struct wally_psbt *psbt; + + if (!fromwire_channeld_splice_confirmed_init(tmpctx, msg, &psbt)) { + channel_internal_error(channel, + "bad splice_confirmed_init %s", + tal_hex(channel, msg)); + return; + } + + cc = splice_command_for_chan(ld, channel); + if (!cc) { + channel_internal_error(channel, "splice_confirmed_init" + " received without an active command %s", + tal_hex(channel, msg)); + return; + } + + struct json_stream *response = json_stream_success(cc->cmd); + json_add_string(response, "psbt", psbt_to_b64(tmpctx, psbt)); + + was_pending(command_success(cc->cmd, response)); +} + +/* Channeld sends us this in response to a user's `splice_update` request */ +static void handle_splice_confirmed_update(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct splice_command *cc; + struct wally_psbt *psbt; + bool commitments_secured; + + if (!fromwire_channeld_splice_confirmed_update(tmpctx, + msg, + &psbt, + &commitments_secured)) { + channel_internal_error(channel, + "bad splice_confirmed_update %s", + tal_hex(channel, msg)); + return; + } + + cc = splice_command_for_chan(ld, channel); + if (!cc) { + channel_internal_error(channel, "splice_update_confirmed" + " received without an active command %s", + tal_hex(channel, msg)); + return; + } + + struct json_stream *response = json_stream_success(cc->cmd); + json_add_string(response, "psbt", psbt_to_b64(tmpctx, psbt)); + json_add_bool(response, "commitments_secured", commitments_secured); + + was_pending(command_success(cc->cmd, response)); +} + +/* Channeld uses this to request the funding transaction for help building the + * splice tx */ +static void handle_splice_lookup_tx(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct bitcoin_txid txid; + struct bitcoin_tx *tx; + u8 *outmsg; + + if (!fromwire_channeld_splice_lookup_tx(msg, &txid)) { + channel_internal_error(channel, + "bad splice_lookup_tx %s", + tal_hex(channel, msg)); + return; + } + + tx = wallet_transaction_get(tmpctx, ld->wallet, &txid); + + if (!tx) { + channel_internal_error(channel, + "channel control unable to find txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + &txid)); + return; + } + + outmsg = towire_channeld_splice_lookup_tx_result(NULL, tx); + subd_send_msg(channel->owner, take(outmsg)); +} + +/* Extra splice data we want to store for bitcoin send tx interface */ +struct send_splice_info +{ + struct splice_command *cc; + struct channel *channel; + const struct bitcoin_tx *final_tx; + u32 output_index; + const char *err_msg; +}; + +static void handle_tx_broadcast(struct send_splice_info *info) +{ + struct lightningd *ld = info->channel->peer->ld; + struct amount_sat unused; + struct json_stream *response; + struct bitcoin_txid txid; + u8 *tx_bytes; + int num_utxos; + + tx_bytes = linearize_tx(tmpctx, info->final_tx); + bitcoin_txid(info->final_tx, &txid); + + /* This might have spent UTXOs from our wallet */ + num_utxos = wallet_extract_owned_outputs(ld->wallet, + info->final_tx->wtx, false, + NULL, &unused); + if (num_utxos) + wallet_transaction_add(ld->wallet, info->final_tx->wtx, 0, 0); + + if (info->cc) { + response = json_stream_success(info->cc->cmd); + + json_add_hex(response, "tx", tx_bytes, tal_bytelen(tx_bytes)); + json_add_txid(response, "txid", &txid); + + was_pending(command_success(info->cc->cmd, response)); + } +} + +/* Succeeds if the utxo was found in the mempool or in the utxo set. If it's in + * a block and spent it will fail but we're okay with that here. */ +static void check_utxo_block(struct bitcoind *bitcoind UNUSED, + const struct bitcoin_tx_output *txout, + void *arg) +{ + struct send_splice_info *info = arg; + + if(!txout) { + if (info->cc) + was_pending(command_fail(info->cc->cmd, + SPLICE_BROADCAST_FAIL, + "Error broadcasting splice " + "tx: %s. Unsent tx discarded " + "%s.", + info->err_msg, + type_to_string(tmpctx, + struct wally_tx, + info->final_tx->wtx))); + + log_unusual(info->channel->log, + "Error broadcasting splice " + "tx: %s. Unsent tx discarded " + "%s.", + info->err_msg, + type_to_string(tmpctx, struct wally_tx, + info->final_tx->wtx)); + } + else + handle_tx_broadcast(info); + + tal_free(info); +} + +/* Callback for after the splice tx is sent to bitcoind */ +static void send_splice_tx_done(struct bitcoind *bitcoind UNUSED, + bool success, const char *msg, + struct send_splice_info *info) +{ + /* A NULL value of `info->cc` means we got here without user intiation. + * This means we are the ACCEPTER side of the splice */ + struct lightningd *ld = info->channel->peer->ld; + struct bitcoin_outpoint outpoint; + + bitcoin_txid(info->final_tx, &outpoint.txid); + outpoint.n = info->output_index; + + if (!success) { + info->err_msg = tal_strdup(info, msg); + bitcoind_getutxout(ld->topology->bitcoind, &outpoint, + check_utxo_block, info); + } else { + handle_tx_broadcast(info); + tal_free(info); + } +} + +/* Where the splice tx gets finally transmitted to the chain */ +static void send_splice_tx(struct channel *channel, + const struct bitcoin_tx *tx, + struct splice_command *cc, + u32 output_index) +{ + struct lightningd *ld = channel->peer->ld; + u8* tx_bytes = linearize_tx(tmpctx, tx); + + log_debug(channel->log, + "Broadcasting splice tx %s for channel %s.", + tal_hex(tmpctx, tx_bytes), + type_to_string(tmpctx, struct channel_id, &channel->cid)); + + struct send_splice_info *info = tal(NULL, struct send_splice_info); + + info->cc = tal_steal(info, cc); + info->channel = channel; + info->final_tx = tal_steal(info, tx); + info->output_index = output_index; + info->err_msg = NULL; + + bitcoind_sendrawtx(ld->topology->bitcoind, + cc ? cc->cmd->id : NULL, + tal_hex(tmpctx, tx_bytes), + false, + send_splice_tx_done, info); +} + +/* After user signs PSBT with splice_signed, our node goes through the signing + * process (adding it's own signatures and peers' sigs), sending the result to + * us here: */ +static void handle_splice_confirmed_signed(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct splice_command *cc; + struct bitcoin_tx *tx; + struct bitcoin_txid txid; + struct channel_inflight *inflight; + u32 output_index; + + if (!fromwire_channeld_splice_confirmed_signed(tmpctx, msg, &tx, &output_index)) { + + channel_internal_error(channel, + "bad splice_confirmed_signed %s", + tal_hex(channel, msg)); + return; + } + + bitcoin_txid(tx, &txid); + inflight = channel_inflight_find(channel, &txid); + if (!inflight) + channel_internal_error(channel, "Unable to load inflight for" + " splice_confirmed_signed txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + &txid)); + + inflight->remote_tx_sigs = true; + wallet_inflight_save(ld->wallet, inflight); + + if (channel->state != CHANNELD_NORMAL) { + log_debug(channel->log, + "Would broadcast splice, but state %s" + " isn't CHANNELD_NORMAL", + channel_state_name(channel)); + return; + } + + cc = splice_command_for_chan(ld, channel); + /* If matching user command found, this was a user intiated splice */ + channel_set_state(channel, + CHANNELD_NORMAL, + CHANNELD_AWAITING_SPLICE, + cc ? REASON_USER : REASON_REMOTE, + "Broadcasting splice"); + + send_splice_tx(channel, tx, cc, output_index); +} + +static void handle_add_inflight(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct bitcoin_outpoint outpoint; + u32 feerate; + struct amount_sat satoshis; + s64 splice_amnt; + struct wally_psbt *psbt; + struct channel_inflight *inflight; + struct bitcoin_signature last_sig; + bool i_am_initiator; + + if (!fromwire_channeld_add_inflight(tmpctx, + msg, + &outpoint.txid, + &outpoint.n, + &feerate, + &satoshis, + &splice_amnt, + &psbt, + &i_am_initiator)) { + channel_internal_error(channel, + "bad channel_add_inflight %s", + tal_hex(channel, msg)); + return; + } + + /* FIXME: DTODO: Use a pointer to a sig instead of zero'ing one out. */ + memset(&last_sig, 0, sizeof(last_sig)); + + inflight = new_inflight(channel, + &outpoint, + feerate, + satoshis, + channel->our_funds, + psbt, + NULL, + last_sig, + channel->lease_expiry, + channel->lease_commit_sig, + channel->lease_chan_max_msat, + channel->lease_chan_max_ppt, + 0, + AMOUNT_MSAT(0), + AMOUNT_SAT(0), + splice_amnt, + i_am_initiator); + + log_debug(channel->log, "lightningd adding inflight with txid %s", + type_to_string(tmpctx, struct bitcoin_txid, + &inflight->funding->outpoint.txid)); + + wallet_inflight_add(ld->wallet, inflight); + channel_watch_inflight(ld, channel, inflight); + + subd_send_msg(channel->owner, take(towire_channeld_got_inflight(NULL))); +} + +static void handle_update_inflight(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct channel_inflight *inflight; + struct wally_psbt *psbt; + struct bitcoin_txid txid; + struct bitcoin_tx *last_tx; + struct bitcoin_signature *last_sig; + + if (!fromwire_channeld_update_inflight(tmpctx, msg, &psbt, &last_tx, + &last_sig)) { + channel_internal_error(channel, + "bad channel_add_inflight %s", + tal_hex(channel, msg)); + return; + } + + psbt_txid(tmpctx, psbt, &txid, NULL); + inflight = channel_inflight_find(channel, &txid); + if (!inflight) + channel_internal_error(channel, "Unable to load inflight for" + " update_inflight txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + &txid)); + + if (!!last_tx != !!last_sig) + channel_internal_error(channel, "Must set last_tx and last_sig" + " together at the same time for" + " update_inflight txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + &txid)); + + if (last_tx) + inflight->last_tx = tal_steal(inflight, last_tx); + + if (last_sig) + inflight->last_sig = *last_sig; + + tal_wally_start(); + if (wally_psbt_combine(inflight->funding_psbt, psbt) != WALLY_OK) { + channel_internal_error(channel, + "Unable to combine PSBTs: %s, %s", + type_to_string(tmpctx, + struct wally_psbt, + inflight->funding_psbt), + type_to_string(tmpctx, + struct wally_psbt, + psbt)); + tal_wally_end(inflight->funding_psbt); + return; + } + tal_wally_end(inflight->funding_psbt); + + psbt_finalize(inflight->funding_psbt); + wallet_inflight_save(ld->wallet, inflight); +} + void channel_record_open(struct channel *channel, u32 blockheight, bool record_push) { struct chain_coin_mvt *mvt; @@ -206,7 +728,8 @@ void channel_record_open(struct channel *channel, u32 blockheight, bool record_p channel->opener == REMOTE)); } -static void lockin_complete(struct channel *channel) +static void lockin_complete(struct channel *channel, + enum channel_state expected_state) { if (!channel->scid && (!channel->alias[REMOTE] || !channel->alias[LOCAL])) { @@ -219,14 +742,18 @@ static void lockin_complete(struct channel *channel) assert(channel->remote_channel_ready); /* We might have already started shutting down */ - if (channel->state != CHANNELD_AWAITING_LOCKIN) { + if (channel->state != expected_state) { log_debug(channel->log, "Lockin complete, but state %s", channel_state_name(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, - CHANNELD_AWAITING_LOCKIN, + expected_state, CHANNELD_NORMAL, REASON_UNKNOWN, "Lockin complete"); @@ -262,6 +789,49 @@ bool channel_on_channel_ready(struct channel *channel, return true; } +static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) +{ + struct amount_sat funding_sats; + s64 splice_amnt; + struct channel_inflight *inflight; + struct bitcoin_txid locked_txid; + + if (!fromwire_channeld_got_splice_locked(msg, &funding_sats, + &splice_amnt, + &locked_txid)) { + channel_internal_error(channel, + "bad channel_got_funding_locked %s", + tal_hex(channel, msg)); + return; + } + + channel->our_msat.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + channel->msat_to_us_min.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + channel->msat_to_us_max.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + + inflight = channel_inflight_find(channel, &locked_txid); + if(!inflight) + channel_internal_error(channel, "Unable to load inflight for" + " locked_txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + &locked_txid)); + + wallet_htlcsigs_confirm_inflight(channel->peer->ld->wallet, channel, + &inflight->funding->outpoint); + + update_channel_from_inflight(channel->peer->ld, channel, inflight); + + /* Remember that we got the lockin */ + wallet_channel_save(channel->peer->ld->wallet, channel); + + /* Empty out the inflights */ + log_debug(channel->log, "lightningd, splice_locked clearing inflights"); + wallet_channel_clear_inflights(channel->peer->ld->wallet, channel); + + lockin_complete(channel, CHANNELD_AWAITING_SPLICE); +} + /* We were informed by channeld that channel is ready (reached mindepth) */ static void peer_got_channel_ready(struct channel *channel, const u8 *msg) { @@ -286,7 +856,7 @@ static void peer_got_channel_ready(struct channel *channel, const u8 *msg) wallet_channel_save(channel->peer->ld->wallet, channel); if (channel->depth >= channel->minimum_depth) - lockin_complete(channel); + lockin_complete(channel, CHANNELD_AWAITING_LOCKIN); } static void peer_got_announcement(struct channel *channel, const u8 *msg) @@ -539,6 +1109,22 @@ static void handle_channel_upgrade(struct channel *channel, wallet_channel_save(channel->peer->ld->wallet, channel); } +static bool get_inflight_outpoint_index(struct channel *channel, + const struct bitcoin_txid *txid, + u32 *index) +{ + struct channel_inflight *inflight; + + list_for_each(&channel->inflights, inflight, list) { + if (bitcoin_txid_eq(txid, &inflight->funding->outpoint.txid)) { + *index = inflight->funding->outpoint.n; + return true; + } + } + + return false; +} + static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) { enum channeld_wire t = fromwire_peektype(msg); @@ -587,6 +1173,36 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) 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); + break; + case WIRE_CHANNELD_SPLICE_FEERATE_ERROR: + handle_splice_feerate_error(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_SPLICE_FUNDING_ERROR: + handle_splice_funding_error(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_SPLICE_STATE_ERROR: + handle_splice_state_error(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_SPLICE_CONFIRMED_UPDATE: + handle_splice_confirmed_update(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_SPLICE_LOOKUP_TX: + handle_splice_lookup_tx(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED: + handle_splice_confirmed_signed(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_ADD_INFLIGHT: + handle_add_inflight(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_UPDATE_INFLIGHT: + handle_update_inflight(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_GOT_SPLICE_LOCKED: + handle_peer_splice_locked(sd->channel, msg); + break; case WIRE_CHANNELD_UPGRADED: handle_channel_upgrade(sd->channel, msg); break; @@ -607,11 +1223,16 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_CHANNEL_UPDATE: case WIRE_CHANNELD_DEV_MEMLEAK: case WIRE_CHANNELD_DEV_QUIESCE: + case WIRE_CHANNELD_GOT_INFLIGHT: /* Replies go to requests. */ case WIRE_CHANNELD_OFFER_HTLC_REPLY: case WIRE_CHANNELD_DEV_REENABLE_COMMIT_REPLY: case WIRE_CHANNELD_DEV_MEMLEAK_REPLY: case WIRE_CHANNELD_SEND_ERROR: + case WIRE_CHANNELD_SPLICE_INIT: + case WIRE_CHANNELD_SPLICE_UPDATE: + case WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT: + case WIRE_CHANNELD_SPLICE_SIGNED: case WIRE_CHANNELD_DEV_QUIESCE_REPLY: break; } @@ -637,6 +1258,9 @@ bool peer_start_channeld(struct channel *channel, 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; + struct bitcoin_txid txid; hsmfd = hsm_get_client_fd(ld, &channel->peer->id, channel->dbid, @@ -644,7 +1268,8 @@ bool peer_start_channeld(struct channel *channel, | HSM_CAP_ECDH | HSM_CAP_COMMITMENT_POINT | HSM_CAP_SIGN_REMOTE_TX - | HSM_CAP_SIGN_ONCHAIN_TX); + | HSM_CAP_SIGN_ONCHAIN_TX + | HSM_CAP_SIGN_CLOSING_TX); channel_set_owner(channel, new_channel_subd(channel, ld, @@ -766,6 +1391,22 @@ bool peer_start_channeld(struct channel *channel, curr_blockheight = last_height; } + inflights = tal_arr(tmpctx, struct inflight *, 0); + list_for_each(&channel->inflights, inflight, list) { + struct inflight *infcopy = tal(inflights, struct inflight); + + infcopy->outpoint = inflight->funding->outpoint; + infcopy->amnt = inflight->funding->total_funds; + infcopy->splice_amnt = inflight->funding->splice_amnt; + infcopy->last_tx = tal_dup(infcopy, struct bitcoin_tx, inflight->last_tx); + infcopy->last_sig = inflight->last_sig; + infcopy->i_am_initiator = inflight->i_am_initiator; + tal_wally_start(); + wally_psbt_clone_alloc(inflight->funding_psbt, 0, &infcopy->psbt); + tal_wally_end_onto(infcopy, infcopy->psbt, struct wally_psbt); + tal_arr_expand(&inflights, infcopy); + } + initmsg = towire_channeld_init(tmpctx, chainparams, ld->our_features, @@ -836,7 +1477,9 @@ bool peer_start_channeld(struct channel *channel, pbases, reestablish_only, channel->channel_update, - ld->experimental_upgrade_protocol); + ld->experimental_upgrade_protocol, + cast_const2(const struct inflight **, + inflights)); /* We don't expect a response: we are triggered by funding_depth_cb. */ subd_send_msg(channel->owner, take(initmsg)); @@ -849,10 +1492,14 @@ bool peer_start_channeld(struct channel *channel, get_block_height(ld->topology)); } + /* FIXME: DTODO: Use a pointer to a txid instead of zero'ing one out. */ + memset(&txid, 0, sizeof(txid)); + /* Artificial confirmation event for zeroconf */ subd_send_msg(channel->owner, take(towire_channeld_funding_depth( - NULL, channel->scid, channel->alias[LOCAL], 0))); + NULL, channel->scid, channel->alias[LOCAL], 0, false, + &txid))); return true; } @@ -862,6 +1509,8 @@ bool channel_tell_depth(struct lightningd *ld, u32 depth) { const char *txidstr; + struct txlocator *loc; + u32 outnum; txidstr = type_to_string(tmpctx, struct bitcoin_txid, txid); channel->depth = depth; @@ -873,6 +1522,35 @@ bool channel_tell_depth(struct lightningd *ld, return false; } + if (channel->state == CHANNELD_AWAITING_SPLICE + && depth >= channel->minimum_depth) { + if (!get_inflight_outpoint_index(channel, txid, &outnum)) { + log_debug(channel->log, "Can't locate splice inflight" + " txid %s", txidstr); + return false; + } + + loc = wallet_transaction_locate(tmpctx, ld->wallet, txid); + if (!loc) { + channel_fail_permanent(channel, + REASON_LOCAL, + "Can't locate splice transaction" + " in wallet txid %s", txidstr); + return false; + } + + if (!mk_short_channel_id(channel->scid, + loc->blkheight, loc->index, + outnum)) { + channel_fail_permanent(channel, + REASON_LOCAL, + "Invalid splice scid %u:%u:%u", + loc->blkheight, loc->index, + channel->funding.n); + return false; + } + } + if (streq(channel->owner->name, "dualopend")) { if (channel->state != DUALOPEND_AWAITING_LOCKIN) { log_debug(channel->log, @@ -888,7 +1566,7 @@ bool channel_tell_depth(struct lightningd *ld, txid, depth); return true; } else if (channel->state != CHANNELD_AWAITING_LOCKIN - && channel->state != CHANNELD_NORMAL) { + && !channel_state_normalish(channel)) { /* If not awaiting lockin/announce, it doesn't * care any more */ log_debug(channel->log, @@ -897,14 +1575,19 @@ bool channel_tell_depth(struct lightningd *ld, return true; } + log_debug(channel->log, + "Sending towire_channeld_funding_depth with channel state %s", + channel_state_str(channel->state)); + subd_send_msg(channel->owner, take(towire_channeld_funding_depth( - NULL, channel->scid, channel->alias[LOCAL], depth))); + NULL, channel->scid, channel->alias[LOCAL], depth, + channel->state == CHANNELD_AWAITING_SPLICE, txid))); if (channel->remote_channel_ready && channel->state == CHANNELD_AWAITING_LOCKIN && depth >= channel->minimum_depth) { - lockin_complete(channel); + lockin_complete(channel, CHANNELD_AWAITING_LOCKIN); } else if (depth == 1 && channel->minimum_depth == 0) { /* If we have a zeroconf channel, i.e., no scid yet * but have exchange `channel_ready` messages, then we @@ -1162,6 +1845,232 @@ void channel_replace_update(struct channel *channel, u8 *update TAKES) channel->channel_update))); } +static struct command_result *param_channel_for_splice(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct channel **channel) +{ + struct command_result *result; + struct channel_id *cid; + + result = param_channel_id(cmd, name, buffer, tok, &cid); + + if (result != NULL) + return result; + + *channel = channel_by_cid(cmd->ld, cid); + if (!*channel) + return command_fail(cmd, SPLICE_UNKNOWN_CHANNEL, + "Unknown channel %s", + type_to_string(tmpctx, struct channel_id, + cid)); + + if (!feature_negotiated(cmd->ld->our_features, + (*channel)->peer->their_features, + OPT_SPLICE)) + return command_fail(cmd, SPLICE_NOT_SUPPORTED, + "splicing not supported"); + + if (!(*channel)->owner) + return command_fail(cmd, SPLICE_WRONG_OWNER, + "Channel is disconnected"); + + if (!streq((*channel)->owner->name, "channeld")) + return command_fail(cmd, + SPLICE_WRONG_OWNER, + "Channel hasn't finished connecting or in " + "abnormal owner state %s", + (*channel)->owner->name); + + if ((*channel)->state != CHANNELD_NORMAL) + return command_fail(cmd, + SPLICE_INVALID_CHANNEL_STATE, + "Channel needs to be in normal state but " + "is in state %s", + channel_state_name(*channel)); + + return NULL; +} + +static void destroy_splice_command(struct splice_command *cc) +{ + list_del(&cc->list); +} + +static struct command_result *json_splice_init(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct channel *channel; + struct splice_command *cc; + struct wally_psbt *initialpsbt; + s64 *relative_amount; + u32 *feerate_per_kw; + bool *force_feerate; + u8 *msg; + + if (!param(cmd, buffer, params, + p_req("channel_id", param_channel_for_splice, &channel), + p_req("relative_amount", param_s64, &relative_amount), + p_opt("initialpsbt", param_psbt, &initialpsbt), + p_opt("feerate_per_kw", param_feerate, &feerate_per_kw), + p_opt_def("force_feerate", param_bool, &force_feerate, false), + NULL)) + return command_param_failed(); + + if (!feerate_per_kw) { + feerate_per_kw = tal(cmd, u32); + *feerate_per_kw = opening_feerate(cmd->ld->topology); + } + + if (splice_command_for_chan(cmd->ld, channel)) + return command_fail(cmd, + SPLICE_BUSY_ERROR, + "Currently waiting on previous splice" + " command to finish."); + + if (!initialpsbt) + initialpsbt = create_psbt(cmd, 0, 0, 0); + if (!validate_psbt(initialpsbt)) + return command_fail(cmd, + SPLICE_INPUT_ERROR, + "PSBT failed to validate."); + + log_debug(cmd->ld->log, "splice_init input PSBT version %d", + initialpsbt->version); + + cc = tal(cmd, struct splice_command); + + list_add_tail(&cmd->ld->splice_commands, &cc->list); + tal_add_destructor(cc, destroy_splice_command); + + cc->cmd = cmd; + cc->channel = channel; + + msg = towire_channeld_splice_init(NULL, initialpsbt, *relative_amount, + *feerate_per_kw, *force_feerate); + + subd_send_msg(channel->owner, take(msg)); + return command_still_pending(cmd); +} + +static struct command_result *json_splice_update(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct channel *channel; + struct splice_command *cc; + struct wally_psbt *psbt; + + if (!param(cmd, buffer, params, + p_req("channel_id", param_channel_for_splice, &channel), + p_req("psbt", param_psbt, &psbt), + NULL)) + return command_param_failed(); + + if (splice_command_for_chan(cmd->ld, channel)) + return command_fail(cmd, + SPLICE_BUSY_ERROR, + "Currently waiting on previous splice" + " command to finish."); + if (!validate_psbt(psbt)) + return command_fail(cmd, + SPLICE_INPUT_ERROR, + "PSBT failed to validate."); + + log_debug(cmd->ld->log, "splice_update input PSBT version %d", + psbt->version); + + cc = tal(cmd, struct splice_command); + + list_add_tail(&cmd->ld->splice_commands, &cc->list); + tal_add_destructor(cc, destroy_splice_command); + + cc->cmd = cmd; + cc->channel = channel; + + subd_send_msg(channel->owner, + take(towire_channeld_splice_update(NULL, psbt))); + return command_still_pending(cmd); +} + +static struct command_result *json_splice_signed(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + u8 *msg; + struct channel *channel; + struct splice_command *cc; + struct wally_psbt *psbt; + bool *sign_first; + + if (!param(cmd, buffer, params, + p_req("channel_id", param_channel_for_splice, &channel), + p_req("psbt", param_psbt, &psbt), + p_opt_def("sign_first", param_bool, &sign_first, false), + NULL)) + return command_param_failed(); + + if (splice_command_for_chan(cmd->ld, channel)) + return command_fail(cmd, + SPLICE_BUSY_ERROR, + "Currently waiting on previous splice" + " command to finish."); + if (!validate_psbt(psbt)) + return command_fail(cmd, + SPLICE_INPUT_ERROR, + "PSBT failed to validate."); + + log_debug(cmd->ld->log, "splice_signed input PSBT version %d", + psbt->version); + + cc = tal(cmd, struct splice_command); + + list_add_tail(&cmd->ld->splice_commands, &cc->list); + tal_add_destructor(cc, destroy_splice_command); + + cc->cmd = cmd; + cc->channel = channel; + + msg = towire_channeld_splice_signed(tmpctx, psbt, *sign_first); + subd_send_msg(channel->owner, take(msg)); + return command_still_pending(cmd); +} + +static const struct json_command splice_init_command = { + "splice_init", + "channels", + json_splice_init, + "Init a channel splice to {channel_id} for {relative_amount} satoshis with {initialpsbt}. " + "Returns updated {psbt} with (partial) contributions from peer" +}; +AUTODATA(json_command, &splice_init_command); + +static const struct json_command splice_update_command = { + "splice_update", + "channels", + json_splice_update, + "Update {channel_id} currently active negotiated splice with {psbt}. " + "" + "Returns updated {psbt} with (partial) contributions from peer. " + "If {commitments_secured} is true, next call may be to splicechannel_finalize, " + "otherwise keep calling splice_update passing back in the returned PSBT until " + "{commitments_secured} is true." +}; +AUTODATA(json_command, &splice_update_command); + +static const struct json_command splice_signed_command = { + "splice_signed", + "channels", + json_splice_signed, + "Send our {signed_psbt}'s tx sigs for {channel_id}." +}; +AUTODATA(json_command, &splice_signed_command); + #if DEVELOPER static struct command_result *json_dev_feerate(struct command *cmd, const char *buffer, @@ -1187,7 +2096,7 @@ static struct command_result *json_dev_feerate(struct command *cmd, return command_fail(cmd, LIGHTNINGD, "Peer not connected"); channel = peer_any_active_channel(peer, &more_than_one); - if (!channel || !channel->owner || channel->state != CHANNELD_NORMAL) + if (!channel || !channel->owner || !channel_state_normalish(channel)) return command_fail(cmd, LIGHTNINGD, "Peer bad state"); /* This is a dev command: fix the api if you need this! */ if (more_than_one) @@ -1247,7 +2156,7 @@ static struct command_result *json_dev_quiesce(struct command *cmd, /* FIXME: If this becomes a real API, check for OPT_QUIESCE! */ channel = peer_any_active_channel(peer, &more_than_one); - if (!channel || !channel->owner || channel->state != CHANNELD_NORMAL) + if (!channel || !channel->owner || !channel_state_normalish(channel)) return command_fail(cmd, LIGHTNINGD, "Peer bad state"); /* This is a dev command: fix the api if you need this! */ if (more_than_one) diff --git a/lightningd/channel_state.h b/lightningd/channel_state.h index bb871fbb9dec..8ba616fbeb7a 100644 --- a/lightningd/channel_state.h +++ b/lightningd/channel_state.h @@ -40,8 +40,11 @@ enum channel_state { /* Dual-funded channel, waiting for lock-in */ DUALOPEND_AWAITING_LOCKIN, + + /* Channel has started splice and is awaiting lock-in */ + CHANNELD_AWAITING_SPLICE, }; -#define CHANNEL_STATE_MAX DUALOPEND_AWAITING_LOCKIN +#define CHANNEL_STATE_MAX CHANNELD_AWAITING_SPLICE /* These are in the database, so don't renumber them! */ enum state_change { diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index f5ba5babb281..eaad43efd807 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -836,6 +836,7 @@ static struct command_result *json_close(struct command *cmd, * waiting) */ switch (channel->state) { case CHANNELD_NORMAL: + case CHANNELD_AWAITING_SPLICE: case CHANNELD_AWAITING_LOCKIN: case DUALOPEND_AWAITING_LOCKIN: channel_set_state(channel, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 91599d699f9f..eb0f9b7927e7 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1186,7 +1186,9 @@ wallet_update_channel(struct lightningd *ld, channel->lease_chan_max_ppt, lease_blockheight_start, channel->push, - lease_amt); + lease_amt, + 0, + false); wallet_inflight_add(ld->wallet, inflight); return inflight; @@ -1338,7 +1340,9 @@ wallet_commit_channel(struct lightningd *ld, channel->lease_chan_max_ppt, lease_blockheight_start, channel->push, - lease_amt); + lease_amt, + 0, + false); wallet_inflight_add(ld->wallet, inflight); /* We might have disconnected and decided we didn't need to diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 14f340e94a28..c4e660647d2f 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -166,6 +166,12 @@ struct ext_key *hsm_init(struct lightningd *ld) fatal("--experimental-anchors needs HSM capable of signing anchors!"); } + if (feature_offered(ld->our_features->bits[INIT_FEATURE], + WIRE_HSMD_SIGN_SPLICE_TX) + && !hsm_capable(ld, WIRE_HSMD_SIGN_SPLICE_TX)) { + fatal("--experimental-splicing needs HSM capable of signing splices!"); + } + /* This is equivalent to makesecret("bolt12-invoice-base") */ msg = towire_hsmd_derive_secret(NULL, tal_dup_arr(tmpctx, u8, (const u8 *)INVOICE_PATH_BASE_STRING, diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index d75be08839fb..9e4f137d4f97 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -217,6 +217,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) list_head_init(&ld->close_commands); list_head_init(&ld->ping_commands); list_head_init(&ld->disconnect_commands); + list_head_init(&ld->splice_commands); list_head_init(&ld->waitblockheight_commands); list_head_init(&ld->wait_commands); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 67a9b76d8dea..37830d029168 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -241,6 +241,9 @@ struct lightningd { /* Outstanding wait commands */ struct list_head wait_commands; + /* Outstanding splice commands. */ + struct list_head splice_commands; + /* Maintained by invoices.c */ struct invoices *invoices; diff --git a/lightningd/options.c b/lightningd/options.c index 69e0c66d5ae5..cd1b1a464b09 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1188,6 +1188,20 @@ static char *opt_set_dual_fund(struct lightningd *ld) return NULL; } +static char *opt_set_splicing(struct lightningd *ld) +{ + /* Splicing requires STFU to be enabled */ + feature_set_or(ld->our_features, + take(feature_set_for_feature(NULL, + OPTIONAL_FEATURE(OPT_QUIESCE)))); + feature_set_or(ld->our_features, + take(feature_set_for_feature(NULL, + OPTIONAL_FEATURE(OPT_SPLICE)))); + /* Splicing requires dual-fund to be enabled */ + opt_set_dual_fund(ld); + return NULL; +} + static char *opt_set_onion_messages(struct lightningd *ld) { feature_set_or(ld->our_features, @@ -1390,6 +1404,11 @@ static void register_opts(struct lightningd *ld) " and allow peers to establish channels" " via v2 channel open protocol."); + opt_register_early_noarg("--experimental-splicing", + opt_set_splicing, ld, + "experimental: Enables the ability to resize" + " channels using splicing"); + /* This affects our features, so set early. */ opt_register_early_noarg("--experimental-onion-messages", opt_set_onion_messages, ld, @@ -1890,6 +1909,11 @@ void add_config_deprecated(struct lightningd *ld, feature_offered(ld->our_features ->bits[INIT_FEATURE], OPT_DUAL_FUND)); + } else if (opt->cb == (void *)opt_set_splicing) { + json_add_bool(response, name0, + feature_offered(ld->our_features + ->bits[INIT_FEATURE], + OPT_SPLICE)); } else if (opt->cb == (void *)opt_set_onion_messages) { json_add_bool(response, name0, feature_offered(ld->our_features diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 229cc3682936..6dc8d2cfca68 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -270,7 +270,7 @@ bool invalid_last_tx(const struct bitcoin_tx *tx) * 0.7.1 release. */ #ifdef COMPAT_V070 /* Old bug had commitment txs with no outputs; bitcoin_txid asserts. */ - return tx->wtx->num_outputs == 0; + return !tx || !tx->wtx || tx->wtx->num_outputs == 0; #else return false; #endif @@ -841,6 +841,9 @@ static void json_add_channel(struct lightningd *ld, json_add_amount_sat_msat(response, "our_funding_msat", inflight->funding->our_funds); + json_add_s64(response, + "splice_amount", + inflight->funding->splice_amnt); /* Add the expected commitment tx id also */ bitcoin_txid(inflight->last_tx, &txid); json_add_txid(response, "scratch_txid", &txid); @@ -1170,6 +1173,7 @@ static void connect_activate_subd(struct lightningd *ld, struct channel *channel case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: + case CHANNELD_AWAITING_SPLICE: case CHANNELD_SHUTTING_DOWN: case CLOSINGD_SIGEXCHANGE: assert(!channel->owner); @@ -1763,16 +1767,33 @@ static bool check_funding_tx(const struct bitcoin_tx *tx, return false; } -static void update_channel_from_inflight(struct lightningd *ld, - struct channel *channel, - const struct channel_inflight *inflight) +void update_channel_from_inflight(struct lightningd *ld, + struct channel *channel, + const struct channel_inflight *inflight) { struct wally_psbt *psbt_copy; channel->funding = inflight->funding->outpoint; channel->funding_sats = inflight->funding->total_funds; + channel->our_funds = inflight->funding->our_funds; + if (!amount_sat_add_sat_s64(&channel->our_funds, channel->our_funds, + inflight->funding->splice_amnt)) { + + channel_fail_permanent(channel, + REASON_LOCAL, + "Updaing channel view for splice causes" + " an invalid satoshi amount wrapping," + " channel: %s, initial funds: %s, splice" + " banace change: %s", + type_to_string(tmpctx, struct channel_id, + &channel->cid), + type_to_string(tmpctx, struct amount_sat, + &channel->our_funds), + inflight->funding->splice_amnt); + } + /* Lease infos ! */ channel->lease_expiry = inflight->lease_expiry; channel->push = inflight->lease_fee; @@ -1827,21 +1848,23 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, tal_free(txidstr); bool min_depth_reached = depth >= channel->minimum_depth; + bool min_depth_no_scid = min_depth_reached && !channel->scid; + bool some_depth_has_scid = depth != 0 && channel->scid; /* Reorg can change scid, so always update/save scid when possible (depth=0 * means the stale block with our funding tx was removed) */ - if ((min_depth_reached && !channel->scid) || (depth && channel->scid)) { + if (min_depth_no_scid || some_depth_has_scid) { struct txlocator *loc; struct channel_inflight *inf; /* Update the channel's info to the correct tx, if needed to * It's possible an 'inflight' has reached depth */ - if (!list_empty(&channel->inflights)) { + if (channel->state != CHANNELD_AWAITING_SPLICE + && !list_empty(&channel->inflights)) { inf = channel_inflight_find(channel, txid); if (!inf) { - channel_fail_permanent(channel, - REASON_LOCAL, - "Txid %s for channel" + log_debug(channel->log, + "Ignoring event for txid %s for channel" " not found in inflights. (peer %s)", type_to_string(tmpctx, struct bitcoin_txid, @@ -1870,8 +1893,9 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, /* If we restart, we could already have peer->scid from database, * we don't need to update scid for stub channels(1x1x1) */ - if (!channel->scid) { - channel->scid = tal(channel, struct short_channel_id); + if (!channel->scid || channel->state == CHANNELD_AWAITING_SPLICE) { + if(!channel->scid) + channel->scid = tal(channel, struct short_channel_id); *channel->scid = scid; wallet_channel_save(ld->wallet, channel); @@ -1922,10 +1946,22 @@ static enum watch_result funding_spent(struct channel *channel, const struct block *block) { struct bitcoin_txid txid; + struct channel_inflight *inflight; + bitcoin_txid(tx, &txid); wallet_channeltxs_add(channel->peer->ld->wallet, channel, WIRE_ONCHAIND_INIT, &txid, 0, block->height); + + /* If we're doing a splice, we expect the funding transaction to be + * spent, so don't freak out and just keep watching in that case */ + list_for_each(&channel->inflights, inflight, list) { + if (bitcoin_txid_eq(&txid, + &inflight->funding->outpoint.txid)) { + return KEEP_WATCHING; + } + } + return onchaind_funding_spent(channel, tx, block->height); } @@ -1953,7 +1989,7 @@ void channel_watch_funding(struct lightningd *ld, struct channel *channel) channel_watch_wrong_funding(ld, channel); } -static void channel_watch_inflight(struct lightningd *ld, +void channel_watch_inflight(struct lightningd *ld, struct channel *channel, struct channel_inflight *inflight) { @@ -2632,6 +2668,7 @@ static struct command_result *param_channel_or_all(struct command *cmd, *channels = tal_arr(cmd, struct channel *, 0); list_for_each(&peer->channels, channel, list) { if (channel->state != CHANNELD_NORMAL + && channel->state != CHANNELD_AWAITING_SPLICE && channel->state != CHANNELD_AWAITING_LOCKIN && channel->state != DUALOPEND_AWAITING_LOCKIN) continue; diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 70eaf6bf7527..a6a8d535d767 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -114,7 +114,14 @@ void resend_closing_transactions(struct lightningd *ld); void drop_to_chain(struct lightningd *ld, struct channel *channel, bool cooperative); +void update_channel_from_inflight(struct lightningd *ld, + struct channel *channel, + const struct channel_inflight *inflight); + void channel_watch_funding(struct lightningd *ld, struct channel *channel); +void channel_watch_inflight(struct lightningd *ld, + struct channel *channel, + struct channel_inflight *inflight); /* If this channel has a "wrong funding" shutdown, watch that too. */ void channel_watch_wrong_funding(struct lightningd *ld, struct channel *channel); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 21e6cf3ababa..0688099cb129 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -582,7 +582,7 @@ static void htlc_offer_timeout(struct htlc_out *out) assert(out->hstate == SENT_ADD_HTLC); /* If owner died, we should already be taken care of. */ - if (!channel->owner || channel->state != CHANNELD_NORMAL) + if (!channel->owner || !channel_state_normalish(channel)) return; log_unusual(channel->owner->log, @@ -2009,6 +2009,8 @@ static bool peer_save_commitsig_received(struct channel *channel, u64 commitnum, channel->next_index[LOCAL]++; + /* DTODO: Add inflight_commit_sigs to the DB */ + /* Update channel->last_sig and channel->last_tx before saving to db */ channel_set_last_tx(channel, tx, commit_sig); @@ -2022,7 +2024,7 @@ static bool peer_save_commitsig_sent(struct channel *channel, u64 commitnum) if (commitnum != channel->next_index[REMOTE]) { channel_internal_error(channel, "channel_sent_commitsig: expected commitnum %"PRIu64 - " got %"PRIu64, + " got %"PRIu64" (while sending commitsig)", channel->next_index[REMOTE], commitnum); return false; } @@ -2252,6 +2254,8 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) struct failed_htlc **failed; struct changed_htlc *changed; struct bitcoin_tx *tx; + struct commitsig **inflight_commit_sigs; + struct channel_inflight *inflight; size_t i; struct lightningd *ld = channel->peer->ld; @@ -2265,7 +2269,8 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) &fulfilled, &failed, &changed, - &tx) + &tx, + &inflight_commit_sigs) || !fee_states_valid(fee_states, channel->opener) || !height_states_valid(blockheight_states, channel->opener)) { channel_internal_error(channel, @@ -2296,11 +2301,25 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) log_debug(channel->log, "got commitsig %"PRIu64 - ": feerate %u, blockheight: %u, %zu added, %zu fulfilled, %zu failed, %zu changed", + ": feerate %u, blockheight: %u, %zu added, %zu fulfilled, " + "%zu failed, %zu changed. %zu splice commitments.", commitnum, get_feerate(fee_states, channel->opener, LOCAL), get_blockheight(blockheight_states, channel->opener, LOCAL), tal_count(added), tal_count(fulfilled), - tal_count(failed), tal_count(changed)); + tal_count(failed), tal_count(changed), + tal_count(inflight_commit_sigs)); + + i = 0; + list_for_each(&channel->inflights, inflight, list) { + i++; + } + if (i != tal_count(inflight_commit_sigs)) { + channel_internal_error(channel, "Got commitsig with incorrect " + "number of splice commitments. " + "lightningd expects %zu but got %zu.", + i, tal_count(inflight_commit_sigs)); + return; + } /* New HTLCs */ for (i = 0; i < tal_count(added); i++) { @@ -2347,8 +2366,24 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) tal_free(channel->last_htlc_sigs); channel->last_htlc_sigs = tal_steal(channel, htlc_sigs); + /* Delete all HTLCs and add last_htlc_sigs back in */ wallet_htlc_sigs_save(ld->wallet, channel->dbid, channel->last_htlc_sigs); + /* Now append htlc sigs for inflights */ + i = 0; + list_for_each(&channel->inflights, inflight, list) { + struct commitsig *commit = inflight_commit_sigs[i]; + + inflight->last_tx = tal_steal(inflight, commit->tx); + inflight->last_tx->chainparams = chainparams; + inflight->last_sig = commit->commit_signature; + wallet_inflight_save(ld->wallet, inflight); + + wallet_htlc_sigs_add(ld->wallet, channel->dbid, + inflight->funding->outpoint, + commit->htlc_signatures); + i++; + } /* Tell it we've committed, and to go ahead with revoke. */ msg = towire_channeld_got_commitsig_reply(msg); diff --git a/lightningd/routehint.c b/lightningd/routehint.c index f6c5fb960fc6..43fa7580ed98 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -104,14 +104,14 @@ routehint_candidates(const tal_t *ctx, continue; } - /* Check channel is in CHANNELD_NORMAL */ + /* Check channel is in CHANNELD_NORMAL or CHANNELD_AWAITING_SPLICE */ candidate.c = find_channel_by_scid(peer, &r->short_channel_id); /* Try seeing if we should be using a remote alias * instead. The `listpeers` result may have returned * the REMOTE alias, because it is the only scid we * have, and it is mandatory once the channel is in - * CHANNELD_NORMAL. */ + * CHANNELD_NORMAL or CHANNELD_AWAITING_SPLICE. */ if (!candidate.c) candidate.c = find_channel_by_alias(peer, &r->short_channel_id, REMOTE); @@ -126,7 +126,8 @@ routehint_candidates(const tal_t *ctx, continue; } - if (candidate.c->state != CHANNELD_NORMAL) { + if (candidate.c->state != CHANNELD_NORMAL + && candidate.c->state != CHANNELD_AWAITING_SPLICE) { log_debug(ld->log, "%s: abnormal channel", type_to_string(tmpctx, struct short_channel_id, diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 3af8a13c6f52..a016a9ca6c9c 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -540,6 +540,10 @@ void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNN void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, uint64_t value UNNEEDED) { fprintf(stderr, "json_add_u64 called!\n"); abort(); } +/* Generated stub for json_add_s64 */ +void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + int64_t value UNNEEDED) +{ fprintf(stderr, "json_add_s64 called!\n"); abort(); } /* Generated stub for json_add_uncommitted_channel */ void json_add_uncommitted_channel(struct json_stream *response UNNEEDED, const struct uncommitted_channel *uc UNNEEDED, @@ -790,6 +794,12 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint64_t **num UNNEEDED) { fprintf(stderr, "param_u64 called!\n"); abort(); } +/* Generated stub for channel_state_normalish */ +bool channel_state_normalish(const struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_state_normalish called!\n"); abort(); } +/* Generated stub for channel_state_awaitish */ +bool channel_state_awaitish(const struct channel *channel UNNEEDED) +{ fprintf(stderr, "channel_state_awaitish called!\n"); abort(); } /* Generated stub for peer_any_active_channel */ struct channel *peer_any_active_channel(struct peer *peer UNNEEDED, bool *others UNNEEDED) { fprintf(stderr, "peer_any_active_channel called!\n"); abort(); } diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 1fa6a3a87c3c..cbbd8e0d1150 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -593,35 +593,6 @@ static bool is_openers(const struct wally_map *unknowns) return serial_id % 2 == TX_INITIATOR; } -static size_t psbt_input_weight(struct wally_psbt *psbt, - size_t in) -{ - size_t weight; - const struct wally_map_item *redeem_script; - - redeem_script = wally_map_get_integer(&psbt->inputs[in].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04); - - /* txid + txout + sequence */ - weight = (32 + 4 + 4) * 4; - if (redeem_script) { - weight += - (redeem_script->value_len + - (varint_t) varint_size(redeem_script->value_len)) * 4; - } else { - /* zero scriptSig length */ - weight += (varint_t) varint_size(0) * 4; - } - - return weight; -} - -static size_t psbt_output_weight(struct wally_psbt *psbt, - size_t outnum) -{ - return (8 + psbt->outputs[outnum].script_len + - varint_size(psbt->outputs[outnum].script_len)) * 4; -} - static bool find_txout(struct wally_psbt *psbt, const u8 *wscript, u32 *funding_txout) { for (size_t i = 0; i < psbt->num_outputs; i++) { @@ -789,14 +760,14 @@ static char *check_balances(const tal_t *ctx, assert(ok); initiator_weight += - psbt_input_weight(psbt, i); + psbt_input_get_weight(psbt, i); } else { ok = amount_sat_add(&accepter_inputs, accepter_inputs, amt); assert(ok); accepter_weight += - psbt_input_weight(psbt, i); + psbt_input_get_weight(psbt, i); } } tot_output_amt = AMOUNT_SAT(0); @@ -855,13 +826,13 @@ static char *check_balances(const tal_t *ctx, } initiator_weight += - psbt_output_weight(psbt, i); + psbt_output_get_weight(psbt, i); } else { ok = amount_sat_add(&accepter_outs, accepter_outs, amt); assert(ok); accepter_weight += - psbt_output_weight(psbt, i); + psbt_output_get_weight(psbt, i); } } @@ -1022,11 +993,12 @@ static u8 *psbt_to_tx_sigs_msg(const tal_t *ctx, const struct wally_psbt *psbt) { const struct witness **ws = - psbt_to_witnesses(tmpctx, psbt, state->our_role); + psbt_to_witnesses(tmpctx, psbt, state->our_role, -1); return towire_tx_signatures(ctx, &state->channel_id, &state->tx_state->funding.txid, - ws); + ws, + NULL); } static void handle_tx_sigs(struct state *state, const u8 *msg) @@ -1038,10 +1010,12 @@ static void handle_tx_sigs(struct state *state, const u8 *msg) enum tx_role their_role = state->our_role == TX_INITIATOR ? TX_ACCEPTER : TX_INITIATOR; + struct tlv_txsigs_tlvs *txsig_tlvs = tlv_txsigs_tlvs_new(tmpctx); if (!fromwire_tx_signatures(tmpctx, msg, &cid, &txid, cast_const3( struct witness ***, - &witnesses))) + &witnesses), + &txsig_tlvs)) open_err_fatal(state, "Bad tx_signatures %s", tal_hex(msg, msg)); @@ -1447,6 +1421,9 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: break; } @@ -1821,6 +1798,9 @@ static bool run_tx_interactive(struct state *state, case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: open_abort(state, "Unexpected wire message %s", tal_hex(tmpctx, msg)); return false; @@ -1902,7 +1882,7 @@ static u8 *accepter_commits(struct state *state, struct amount_msat our_msats; struct channel_id cid; const u8 *wscript; - u8 *msg; + u8 *msg, *commit_msg; char *error; /* Find the funding transaction txid */ @@ -1940,9 +1920,12 @@ static u8 *accepter_commits(struct state *state, } remote_sig.sighash_type = SIGHASH_ALL; + + struct tlv_commitment_signed_tlvs *cs_tlv + = tlv_commitment_signed_tlvs_new(tmpctx); if (!fromwire_commitment_signed(tmpctx, msg, &cid, &remote_sig.s, - &htlc_sigs)) + &htlc_sigs, &cs_tlv)) open_err_fatal(state, "Parsing commitment signed %s", tal_hex(tmpctx, msg)); @@ -2140,10 +2123,10 @@ static u8 *accepter_commits(struct state *state, master_badmsg(WIRE_DUALOPEND_SEND_TX_SIGS, msg); /* Send our commitment sigs over now */ - peer_write(state->pps, - take(towire_commitment_signed(NULL, - &state->channel_id, - &local_sig.s, NULL))); + commit_msg = towire_commitment_signed(NULL, &state->channel_id, + &local_sig.s, NULL, NULL); + + peer_write(state->pps, take(commit_msg)); tal_free(local_commit); return msg; } @@ -2786,7 +2769,7 @@ static u8 *opener_commits(struct state *state, assert(local_sig.sighash_type == SIGHASH_ALL); msg = towire_commitment_signed(tmpctx, &state->channel_id, &local_sig.s, - NULL); + NULL, NULL); peer_write(state->pps, msg); peer_billboard(false, "channel open: commitment sent, waiting for reply"); @@ -2799,9 +2782,12 @@ static u8 *opener_commits(struct state *state, } remote_sig.sighash_type = SIGHASH_ALL; + + struct tlv_commitment_signed_tlvs *cs_tlv + = tlv_commitment_signed_tlvs_new(tmpctx); if (!fromwire_commitment_signed(tmpctx, msg, &cid, &remote_sig.s, - &htlc_sigs)) + &htlc_sigs, &cs_tlv)) open_err_fatal(state, "Parsing commitment signed %s", tal_hex(tmpctx, msg)); @@ -4213,6 +4199,9 @@ static u8 *handle_peer_in(struct state *state) case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: break; } diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 9f75a4ad4744..b4ae81d6c13d 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3291,7 +3291,8 @@ static struct command_result *direct_pay_listpeerchannels(struct command *cmd, if (!chan->connected) continue; - if (!streq(chan->state, "CHANNELD_NORMAL")) + if (!streq(chan->state, "CHANNELD_NORMAL") + && !streq(chan->state, "CHANNELD_AWAITING_SPLICE")) continue; /* Must have either a local alias for zeroconf diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index c78350126061..94d33085273e 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -160,6 +160,9 @@ bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32 /* Generated stub for json_to_u64 */ bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) { fprintf(stderr, "json_to_u64 called!\n"); abort(); } +/* Generated stub for json_to_s64 */ +bool json_to_s64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, s64 *num UNNEEDED) +{ fprintf(stderr, "json_to_s64 called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } @@ -255,8 +258,12 @@ static void update_connection(int store_fd, bool disable) { secp256k1_ecdsa_signature dummy_sig; + u8 flags = node_id_idx(from, to); u8 *msg; + if (disable) + flags |= ROUTING_FLAGS_DISABLED; + /* So valgrind doesn't complain */ memset(&dummy_sig, 0, sizeof(dummy_sig)); @@ -265,8 +272,7 @@ static void update_connection(int store_fd, &chainparams->genesis_blockhash, scid, 0, ROUTING_OPT_HTLC_MAX_MSAT, - node_id_idx(from, to) - + (disable ? ROUTING_FLAGS_DISABLED : 0), + flags, delay, min, base_fee, diff --git a/plugins/topology.c b/plugins/topology.c index c6def06ced1a..32c2bea4a285 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -317,7 +317,10 @@ static struct node_map *local_connected(const tal_t *ctx, /* Must also have a channel in CHANNELD_NORMAL */ normal_chan = json_tok_streq(buf, json_get_member(buf, channel, "state"), - "CHANNELD_NORMAL"); + "CHANNELD_NORMAL") + || json_tok_streq(buf, + json_get_member(buf, channel, "state"), + "CHANNELD_AWAITING_SPLICE"); if (normal_chan) node_map_add(connected, diff --git a/tests/test_connection.py b/tests/test_connection.py index 04a9c0681f7d..f847a6e93835 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1016,8 +1016,10 @@ def no_blocks_above(req): l2.daemon.wait_for_logs([r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES', r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES']) + count = ''.join(l1.daemon.logs).count(r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES') + # l1 only did send them the first time - assert(''.join(l1.daemon.logs).count(r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES') == 1) + assert(count == 1 or count == 2) @pytest.mark.openchannel('v1') diff --git a/tests/test_misc.py b/tests/test_misc.py index 553d5af19b13..d16a22dc661f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2219,8 +2219,9 @@ def test_list_features_only(node_factory): 'option_shutdown_anysegwit/odd', 'option_channel_type/odd', 'option_scid_alias/odd', - 'option_zeroconf/odd', - 'supports_open_accept_channel_type'] + 'option_zeroconf/odd'] + expected += ['supports_open_accept_channel_type'] + assert features == expected diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b1cf03e0352e..16344423221d 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -3886,6 +3886,8 @@ def test_sql(node_factory, bitcoind): 'type': 'string'}, {'name': 'total_funding_msat', 'type': 'msat'}, + {'name': 'splice_amount', + 'type': 's64'}, {'name': 'our_funding_msat', 'type': 'msat'}, {'name': 'scratch_txid', @@ -4023,6 +4025,7 @@ def test_sql(node_factory, bitcoind): 'u16': 'INTEGER', 'u32': 'INTEGER', 'u64': 'INTEGER', + 's64': 'INTEGER', 'msat': 'INTEGER', 'hex': 'BLOB', 'hash': 'BLOB', @@ -4093,7 +4096,7 @@ def test_sql(node_factory, bitcoind): assert len(bytes.fromhex(val)) == 32 elif col['type'] == "pubkey": assert len(bytes.fromhex(val)) == 33 - elif col['type'] in ("msat", "integer", "u64", "u32", "u16", "u8", "boolean"): + elif col['type'] in ("msat", "integer", "s64", "u64", "u32", "u16", "u8", "boolean"): int(val) elif col['type'] == "number": float(val) diff --git a/tests/test_splicing.py b/tests/test_splicing.py new file mode 100644 index 000000000000..614c0f52257f --- /dev/null +++ b/tests/test_splicing.py @@ -0,0 +1,35 @@ +from fixtures import * # noqa: F401,F403 +import os +import pytest +import unittest + + +@unittest.skipIf(os.environ.get("EXPERIMENTAL_SPLICING", '0') != '1', "Need experimental splicing turned on") +@pytest.mark.openchannel('v2') +def test_splice(node_factory, bitcoind): + l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True) + + chan_id = l1.get_channel_id(l2) + + # add extra sats to pay fee + funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True) + + result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + result = l1.rpc.signpsbt(result['psbt']) + result = l1.rpc.splice_signed(chan_id, result['signed_psbt']) + + l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE') + l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE') + + mempool = bitcoind.rpc.getrawmempool(True) + assert len(list(mempool.keys())) == 1 + assert result['txid'] in list(mempool.keys()) + + bitcoind.generate_block(6, wait_for_mempool=1) + + l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + + inv = l2.rpc.invoice(10**2, '3', 'no_3') + l1.rpc.pay(inv['bolt11']) diff --git a/tests/utils.py b/tests/utils.py index bbea560bfe9c..b2fe0d9bcee3 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,7 +2,7 @@ from pyln.testing.utils import env, only_one, wait_for, write_config, TailableProc, sync_blockheight, wait_channel_quiescent, get_tx_p2wsh_outnum, mine_funding_to_announce, scid_to_int # noqa: F401 import bitstring from pyln.client import Millisatoshi -from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND +from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, EXPERIMENTAL_SPLICING import time COMPAT = env("COMPAT", "1") == "1" @@ -42,6 +42,10 @@ def expected_peer_features(wumbo_channels=False, extra=[]): if EXPERIMENTAL_DUAL_FUND: # option_dual_fund features += [29] + if EXPERIMENTAL_SPLICING: + features += [29] # option_dual_fund + features += [35] # option_quiesce + features += [63] # option_splice return hex_bits(features + extra) @@ -55,6 +59,10 @@ def expected_node_features(wumbo_channels=False, extra=[]): if EXPERIMENTAL_DUAL_FUND: # option_dual_fund features += [29] + if EXPERIMENTAL_SPLICING: + features += [29] # option_dual_fund + features += [35] # option_quiesce + features += [63] # option_splice return hex_bits(features + extra) diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 612ad4b5c69f..92ebf7b9d4a2 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -231,6 +231,7 @@ class Type(FieldSet): 'failed_htlc', 'existing_htlc', 'simple_htlc', + 'inflight', 'utxo', 'bitcoin_tx', 'wirestring', @@ -248,6 +249,7 @@ class Type(FieldSet): 'wally_psbt', 'wally_tx', 'scb_chan', + 'inflight', ] # Some BOLT types are re-typed based on their field name diff --git a/wallet/db.c b/wallet/db.c index b9705373242c..1ec83523247f 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -970,6 +970,11 @@ static struct migration dbmigrations[] = { {SQL("CREATE INDEX invoice_update_idx ON invoices (updated_index)"), NULL}, {NULL, migrate_datastore_commando_runes}, {NULL, migrate_invoice_created_index_var}, + /* Splicing requires us to store HTLC sigs for inflight splices and allows us to discard old sigs after splice confirmation. */ + {SQL("ALTER TABLE htlc_sigs ADD inflight_tx_id BLOB"), NULL}, + {SQL("ALTER TABLE htlc_sigs ADD inflight_tx_outnum INTEGER"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD splice_amnt BIGINT DEFAULT 0"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD i_am_initiator INTEGER DEFAULT 0"), NULL}, }; /** diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index ad8d84eac688..269668a1a244 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -172,7 +172,9 @@ struct channel_inflight *new_inflight(struct channel *channel UNNEEDED, const u16 lease_chan_max_ppt UNNEEDED, const u32 lease_blockheight_start UNNEEDED, const struct amount_msat lease_fee UNNEEDED, - const struct amount_sat lease_amt UNNEEDED) + const struct amount_sat lease_amt UNNEEDED, + s64 splice_amnt UNNEEDED, + bool i_am_initiator UNNEEDED) { fprintf(stderr, "new_inflight called!\n"); abort(); } /* Generated stub for new_logger */ struct logger *new_logger(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 59586848a1ac..f135bd58c2bc 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -158,7 +158,7 @@ void fatal_vfmt(const char *fmt UNNEEDED, va_list ap UNNEEDED) bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_channeld_got_commitsig */ -bool fromwire_channeld_got_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct bitcoin_signature *signature UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, struct added_htlc **added UNNEEDED, struct fulfilled_htlc **fulfilled UNNEEDED, struct failed_htlc ***failed UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_tx **tx UNNEEDED) +bool fromwire_channeld_got_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct bitcoin_signature *signature UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, struct added_htlc **added UNNEEDED, struct fulfilled_htlc **fulfilled UNNEEDED, struct failed_htlc ***failed UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_tx **tx UNNEEDED, struct commitsig ***inflight_commitsigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_got_commitsig called!\n"); abort(); } /* Generated stub for fromwire_channeld_got_revoke */ bool fromwire_channeld_got_revoke(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *revokenum UNNEEDED, struct secret *per_commitment_secret UNNEEDED, struct pubkey *next_per_commit_point UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct penalty_base **pbase UNNEEDED, struct bitcoin_tx **penalty_tx UNNEEDED) @@ -350,6 +350,10 @@ void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNN void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, uint64_t value UNNEEDED) { fprintf(stderr, "json_add_u64 called!\n"); abort(); } +/* Generated stub for json_add_s64 */ +void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + int64_t value UNNEEDED) +{ fprintf(stderr, "json_add_s64 called!\n"); abort(); } /* Generated stub for json_add_uncommitted_channel */ void json_add_uncommitted_channel(struct json_stream *response UNNEEDED, const struct uncommitted_channel *uc UNNEEDED, @@ -1713,7 +1717,9 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) sig, 1, lease_commit_sig, 2, 4, 22, AMOUNT_MSAT(10), - AMOUNT_SAT(1111)); + AMOUNT_SAT(1111), + 0, + false); /* do inflights get correctly added to the channel? */ wallet_inflight_add(w, inflight); @@ -1737,7 +1743,9 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) sig, 0, NULL, 0, 0, 0, AMOUNT_MSAT(0), - AMOUNT_SAT(0)); + AMOUNT_SAT(0), + 0, + false); wallet_inflight_add(w, inflight); CHECK_MSG(c2 = wallet_channel_load(w, chan->dbid), tal_fmt(w, "Load from DB")); diff --git a/wallet/wallet.c b/wallet/wallet.c index e28f59cf8499..c9610fe3d6f0 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -982,7 +982,8 @@ wallet_htlc_sigs_load(const tal_t *ctx, struct wallet *w, u64 channelid, struct bitcoin_signature *htlc_sigs = tal_arr(ctx, struct bitcoin_signature, 0); stmt = db_prepare_v2( - w->db, SQL("SELECT signature FROM htlc_sigs WHERE channelid = ?")); + w->db, SQL("SELECT signature FROM htlc_sigs WHERE channelid = ?" + " AND inflight_tx_id is NULL")); db_bind_u64(stmt, channelid); db_query_prepared(stmt); @@ -1149,8 +1150,10 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) ", lease_blockheight_start" ", lease_fee" ", lease_satoshi" + ", splice_amnt" + ", i_am_initiator" ") VALUES (" - "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, inflight->channel->dbid); db_bind_txid(stmt, &inflight->funding->outpoint.txid); @@ -1160,7 +1163,10 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) db_bind_amount_sat(stmt, &inflight->funding->our_funds); db_bind_psbt(stmt, inflight->funding_psbt); db_bind_int(stmt, inflight->remote_tx_sigs ? 1 : 0); - db_bind_psbt(stmt, inflight->last_tx->psbt); + if (inflight->last_tx) + db_bind_psbt(stmt, inflight->last_tx->psbt); + else + db_bind_null(stmt); db_bind_signature(stmt, &inflight->last_sig.s); if (inflight->lease_expiry != 0) { @@ -1181,6 +1187,9 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) db_bind_int(stmt, 0); } + db_bind_s64(stmt, inflight->funding->splice_amnt); + db_bind_int(stmt, inflight->i_am_initiator); + db_exec_prepared_v2(stmt); assert(!stmt->error); tal_free(stmt); @@ -1192,18 +1201,24 @@ void wallet_inflight_save(struct wallet *w, struct db_stmt *stmt; /* The *only* thing you can update on an * inflight is the funding PSBT (to add sigs) - * ((and maybe later the last_tx/last_sig if this is for - * a splice */ + * and the last_tx/last_sig if this is for a splice */ stmt = db_prepare_v2(w->db, SQL("UPDATE channel_funding_inflights SET" " funding_psbt=?" // 0 ", funding_tx_remote_sigs_received=?" // 1 + ", last_tx=?" // 2 + ", last_sig=?" // 3 " WHERE" - " channel_id=?" // 2 - " AND funding_tx_id=?" // 3 - " AND funding_tx_outnum=?")); // 4 + " channel_id=?" // 4 + " AND funding_tx_id=?" // 5 + " AND funding_tx_outnum=?")); // 6 db_bind_psbt(stmt, inflight->funding_psbt); db_bind_int(stmt, inflight->remote_tx_sigs); + if (inflight->last_tx) + db_bind_psbt(stmt, inflight->last_tx->psbt); + else + db_bind_null(stmt); + db_bind_signature(stmt, &inflight->last_sig.s); db_bind_u64(stmt, inflight->channel->dbid); db_bind_txid(stmt, &inflight->funding->outpoint.txid); db_bind_int(stmt, inflight->funding->outpoint.n); @@ -1239,6 +1254,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, struct bitcoin_signature last_sig; struct bitcoin_tx *last_tx; struct channel_inflight *inflight; + s64 splice_amnt; + bool i_am_initiator; secp256k1_ecdsa_signature *lease_commit_sig; u32 lease_blockheight_start; @@ -1289,6 +1306,9 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, } else last_tx = NULL; + splice_amnt = db_col_s64(stmt, "splice_amnt"); + i_am_initiator = db_col_int(stmt, "i_am_initiator"); + inflight = new_inflight(chan, &funding, db_col_int(stmt, "funding_feerate"), funding_sat, @@ -1302,7 +1322,9 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, lease_chan_max_ppt, lease_blockheight_start, lease_fee, - lease_amt); + lease_amt, + splice_amnt, + i_am_initiator); /* Pull out the serialized tx-sigs-received-ness */ inflight->remote_tx_sigs = db_col_int(stmt, "funding_tx_remote_sigs_received"); @@ -1332,6 +1354,8 @@ static bool wallet_channel_load_inflights(struct wallet *w, ", lease_blockheight_start" ", lease_fee" ", lease_satoshi" + ", splice_amnt" + ", i_am_initiator" " FROM channel_funding_inflights" " WHERE channel_id = ?" " ORDER BY funding_feerate")); @@ -2052,6 +2076,35 @@ void wallet_announcement_save(struct wallet *w, u64 id, db_exec_prepared_v2(take(stmt)); } + +void wallet_htlcsigs_confirm_inflight(struct wallet *w, struct channel *chan, + const struct bitcoin_outpoint *confirmed_outpoint) +{ + struct db_stmt *stmt; + + /* A NULL inflight_tx_id means these htlc_sigs apply to the currently + * active channel */ + stmt = db_prepare_v2(w->db, SQL("DELETE FROM htlc_sigs" + " WHERE channelid=?" + " AND (inflight_tx_id is NULL" + " OR (" + " inflight_tx_id!=?" + " AND " + " inflight_tx_outnum!=?" + ")" + ")")); + db_bind_u64(stmt, chan->dbid); + db_bind_txid(stmt, &confirmed_outpoint->txid); + db_bind_int(stmt, confirmed_outpoint->n); + db_exec_prepared_v2(take(stmt)); + + stmt = db_prepare_v2(w->db, SQL("UPDATE htlc_sigs" + " SET inflight_tx_id=NULL" + " WHERE channelid=?")); + db_bind_u64(stmt, chan->dbid); + db_exec_prepared_v2(take(stmt)); +} + void wallet_channel_save(struct wallet *w, struct channel *chan) { struct db_stmt *stmt; @@ -3799,6 +3852,26 @@ void wallet_htlc_sigs_save(struct wallet *w, u64 channel_id, } } +void wallet_htlc_sigs_add(struct wallet *w, u64 channel_id, + struct bitcoin_outpoint inflight_outpoint, + const struct bitcoin_signature *htlc_sigs) +{ + struct db_stmt *stmt; + + /* Now insert the new ones */ + for (size_t i=0; idb, + SQL("INSERT INTO htlc_sigs (channelid," + " inflight_tx_id, inflight_tx_outnum," + " signature) VALUES (?, ?, ?)")); + db_bind_u64(stmt, channel_id); + db_bind_txid(stmt, &inflight_outpoint.txid); + db_bind_int(stmt, inflight_outpoint.n); + db_bind_signature(stmt, &htlc_sigs[i].s); + db_exec_prepared_v2(take(stmt)); + } +} + bool wallet_sanity_check(struct wallet *w) { struct bitcoin_blkid chainhash; diff --git a/wallet/wallet.h b/wallet/wallet.h index 6aa90a761d01..2f5d65e818fa 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -607,6 +607,9 @@ bool wallet_shachain_add_hash(struct wallet *wallet, */ u64 wallet_get_channel_dbid(struct wallet *wallet); +void wallet_htlcsigs_confirm_inflight(struct wallet *w, struct channel *chan, + const struct bitcoin_outpoint *confirmed_outpoint); + /** * wallet_channel_save -- Upsert the channel into the database * @@ -1005,11 +1008,20 @@ wallet_payments_by_invoice_request(const tal_t *ctx, const struct sha256 *local_invreq_id); /** - * wallet_htlc_sigs_save - Store the latest HTLC sigs for the channel + * wallet_htlc_sigs_save - Delete all HTLC sigs (including inflights) for the + * channel and store `htlc_sigs` as the new values. */ void wallet_htlc_sigs_save(struct wallet *w, u64 channel_id, const struct bitcoin_signature *htlc_sigs); +/** + * wallet_htlc_sigs_add - Appends `htlc_sigs` for the given inflight splice. + * `inflight_id` is the funding txid for the given splice. + */ +void wallet_htlc_sigs_add(struct wallet *w, u64 channel_id, + struct bitcoin_outpoint inflight_outpoint, + const struct bitcoin_signature *htlc_sigs); + /** * wallet_sanity_check - Check that the wallet is setup for this node_id and chain * diff --git a/wire/extracted_peer_06_funding_outpoint_sigs.patch b/wire/extracted_peer_06_funding_outpoint_sigs.patch new file mode 100644 index 000000000000..10f2df60f11f --- /dev/null +++ b/wire/extracted_peer_06_funding_outpoint_sigs.patch @@ -0,0 +1,18 @@ +--- wire/peer_exp_wire.csv 2022-06-22 19:07:24.000000000 -0500 ++++ - 2022-06-30 16:00:51.000000000 -0500 +@@ -65,12 +57,15 @@ + msgdata,tx_signatures,txid,sha256, + msgdata,tx_signatures,num_witnesses,u16, + msgdata,tx_signatures,witness_stack,witness_stack,num_witnesses ++msgdata,tx_signatures,tlvs,txsigs_tlvs, + subtype,witness_stack + subtypedata,witness_stack,num_input_witness,u16, + subtypedata,witness_stack,witness_element,witness_element,num_input_witness + subtype,witness_element + subtypedata,witness_element,len,u16, + subtypedata,witness_element,witness,byte,len ++tlvtype,txsigs_tlvs,funding_outpoint_sig,0 ++tlvdata,txsigs_tlvs,funding_outpoint_sig,sig,byte,... + msgtype,tx_init_rbf,72 + msgdata,tx_init_rbf,channel_id,channel_id, + msgdata,tx_init_rbf,locktime,u32, diff --git a/wire/extracted_peer_10_upgradable.patch b/wire/extracted_peer_10_upgradable.patch index c168586abeca..9adb42f6e268 100644 --- a/wire/extracted_peer_10_upgradable.patch +++ b/wire/extracted_peer_10_upgradable.patch @@ -1,10 +1,12 @@ --- wire/peer_wire.csv 2021-05-09 15:44:59.166135652 +0930 +++ wire/peer_wire.csv.raw 2021-05-11 09:59:31.695459756 +0930 -@@ -244,6 +140,15 @@ +@@ -244,6 +140,17 @@ msgdata,channel_reestablish,next_revocation_number,u64, msgdata,channel_reestablish,your_last_per_commitment_secret,byte,32 msgdata,channel_reestablish,my_current_per_commitment_point,point, +msgdata,channel_reestablish,tlvs,channel_reestablish_tlvs, ++tlvtype,channel_reestablish_tlvs,next_funding,0 ++tlvdata,channel_reestablish_tlvs,next_funding,next_funding_txid,sha256, +tlvtype,channel_reestablish_tlvs,next_to_send,1 +tlvdata,channel_reestablish_tlvs,next_to_send,commitment_number,tu64, +tlvtype,channel_reestablish_tlvs,desired_channel_type,3 diff --git a/wire/fromwire.c b/wire/fromwire.c index 99a0be755155..23810ad3ac90 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -280,3 +280,4 @@ void fromwire_siphash_seed(const u8 **cursor, size_t *max, { fromwire(cursor, max, seed, sizeof(*seed)); } + diff --git a/wire/peer_wire.c b/wire/peer_wire.c index b7d838e1a22e..999b52f71e9e 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -50,6 +50,9 @@ static bool unknown_type(enum peer_wire t) case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: return false; } return true; @@ -104,6 +107,9 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: case WIRE_STFU: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: break; } return false; @@ -365,6 +371,30 @@ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id) * 2. data: * * [`channel_id`:`channel_id`] */ + case WIRE_SPLICE: + /* BOLT-splice #2: + * 1. type: 74 (`splice`) + * 2. data: + * * [`chain_hash`:`chain_hash`] + * * [`channel_id`:`channel_id`] + * * [`u32`:`funding_feerate_perkw`] + * * [`point`:`funding_pubkey`] + */ + case WIRE_SPLICE_ACK: + /* BOLT-splice #2: + * 1. type: 76 (`splice_ack`) + * 2. data: + * * [`chain_hash`:`chain_hash`] + * * [`channel_id`:`channel_id`] + * * [`point`:`funding_pubkey`] + */ + case WIRE_SPLICE_LOCKED: + /* BOLT-splice #2: + * 1. type: 78 (`splice_locked`) + * 2. data: + * * [`chain_hash`:`chain_hash`] + * * [`channel_id`:`channel_id`] + */ return fromwire_channel_id(&cursor, &max, channel_id); } return false; diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 32f8617b5f44..5b5803afce4d 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -66,6 +66,9 @@ msgdata,tx_signatures,witnesses,witness,num_witnesses subtype,witness subtypedata,witness,len,u16, subtypedata,witness,witness_data,byte,len +msgdata,tx_signatures,tlvs,txsigs_tlvs, +tlvtype,txsigs_tlvs,funding_outpoint_sig,0 +tlvdata,txsigs_tlvs,funding_outpoint_sig,sig,byte,... msgtype,tx_init_rbf,72 msgdata,tx_init_rbf,channel_id,channel_id, msgdata,tx_init_rbf,locktime,u32, @@ -203,6 +206,20 @@ subtypedata,lease_rates,channel_fee_max_base_msat,tu32, msgtype,stfu,2 msgdata,stfu,channel_id,channel_id, msgdata,stfu,initiator,u8, +msgtype,splice,75 +msgdata,splice,channel_id,channel_id, +msgdata,splice,chain_hash,chain_hash, +msgdata,splice,relative_satoshis,s64, +msgdata,splice,funding_feerate_perkw,u32, +msgdata,splice,locktime,u32, +msgdata,splice,funding_pubkey,point, +msgtype,splice_ack,76 +msgdata,splice_ack,channel_id,channel_id, +msgdata,splice_ack,chain_hash,chain_hash, +msgdata,splice_ack,relative_satoshis,s64, +msgdata,splice_ack,funding_pubkey,point, +msgtype,splice_locked,77, +msgdata,splice_locked,channel_id,channel_id, msgtype,shutdown,38 msgdata,shutdown,channel_id,channel_id, msgdata,shutdown,len,u16, @@ -247,6 +264,9 @@ msgdata,commitment_signed,channel_id,channel_id, msgdata,commitment_signed,signature,signature, msgdata,commitment_signed,num_htlcs,u16, msgdata,commitment_signed,htlc_signature,signature,num_htlcs +msgdata,commitment_signed,splice_channel_id,commitment_signed_tlvs, +tlvtype,commitment_signed_tlvs,splice_info,0 +tlvdata,commitment_signed_tlvs,splice_info,splice_channel_id,channel_id, msgtype,revoke_and_ack,133 msgdata,revoke_and_ack,channel_id,channel_id, msgdata,revoke_and_ack,per_commitment_secret,byte,32 diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index f29aac378ef2..00a2e51b29c3 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -40,6 +40,11 @@ static void set_scid(struct short_channel_id *scid) memset(scid, 2, sizeof(struct short_channel_id)); } +static void set_cid(struct channel_id *cid) +{ + memset(cid, 2, sizeof(struct channel_id)); +} + /* Size up to field. */ #define upto_field(p, field) \ ((char *)&(p)->field - (char *)(p)) @@ -160,6 +165,7 @@ struct msg_commitment_signed { struct channel_id channel_id; secp256k1_ecdsa_signature signature; secp256k1_ecdsa_signature *htlc_signature; + struct tlv_commitment_signed_tlvs *tlvs; }; struct msg_node_announcement { secp256k1_ecdsa_signature signature; @@ -525,17 +531,20 @@ static void *towire_struct_commitment_signed(const tal_t *ctx, return towire_commitment_signed(ctx, &s->channel_id, &s->signature, - s->htlc_signature); + s->htlc_signature, + s->tlvs); } static struct msg_commitment_signed *fromwire_struct_commitment_signed(const tal_t *ctx, const void *p) { struct msg_commitment_signed *s = tal(ctx, struct msg_commitment_signed); + s->tlvs = tlv_commitment_signed_tlvs_new(ctx); if (!fromwire_commitment_signed(s, p, &s->channel_id, &s->signature, - &s->htlc_signature)) + &s->htlc_signature, + &s->tlvs)) return tal_free(s); return s; } @@ -781,7 +790,8 @@ static bool commitment_signed_eq(const struct msg_commitment_signed *a, const struct msg_commitment_signed *b) { return eq_upto(a, b, htlc_signature) - && eq_var(a, b, htlc_signature); + && eq_var(a, b, htlc_signature) + && eq_tlv(a, b, splice_info, channel_id_eq); } static bool funding_signed_eq(const struct msg_funding_signed *a, @@ -1007,11 +1017,14 @@ int main(int argc, char *argv[]) memset(&cs, 2, sizeof(cs)); cs.htlc_signature = tal_arr(ctx, secp256k1_ecdsa_signature, 2); memset(cs.htlc_signature, 2, sizeof(secp256k1_ecdsa_signature)*2); + cs.tlvs = tlv_commitment_signed_tlvs_new(tmpctx); + cs.tlvs->splice_info = tal(ctx, struct channel_id); + set_cid(cs.tlvs->splice_info); msg = towire_struct_commitment_signed(ctx, &cs); cs2 = fromwire_struct_commitment_signed(ctx, msg); assert(commitment_signed_eq(&cs, cs2)); - test_corruption(&cs, cs2, commitment_signed); + test_corruption_tlv(&cs, cs2, commitment_signed); memset(&fs, 2, sizeof(fs)); diff --git a/wire/towire.c b/wire/towire.c index c3a9a21394be..3a123ed85a55 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -4,6 +4,7 @@ #include #include #include +#include #include void towire(u8 **pptr, const void *data, size_t len)