diff --git a/.dir-locals.el b/.dir-locals.el index 1ca27d4ff271..518359236abd 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -4,4 +4,10 @@ (c-basic-offset . 8) (tab-width . 8) )) -) + + (c++-mode . ((c-file-style . "linux") + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + (c-basic-offset . 8) + (tab-width . 8) + ))) diff --git a/.github/scripts/setup.sh b/.github/scripts/setup.sh index 59a092051897..dc57215668aa 100755 --- a/.github/scripts/setup.sh +++ b/.github/scripts/setup.sh @@ -15,6 +15,7 @@ sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ build-essential \ clang \ cppcheck \ + curl \ docbook-xml \ eatmydata \ gcc-aarch64-linux-gnu \ diff --git a/.msggen.json b/.msggen.json index 4b19ed69ca93..0d4f6417215f 100644 --- a/.msggen.json +++ b/.msggen.json @@ -334,6 +334,16 @@ "complete": 1, "pending": 0 }, + "WaitIndexname": { + "created": 0, + "deleted": 2, + "updated": 1 + }, + "WaitSubsystem": { + "forwards": 1, + "invoices": 0, + "sendpays": 2 + }, "WaitanyinvoiceStatus": { "expired": 1, "paid": 0 @@ -697,6 +707,35 @@ "Feerates.perkw": 3, "Feerates.warning_missing_feerates": 1 }, + "FetchinvoiceChanges": { + "FetchInvoice.changes.amount_msat": 5, + "FetchInvoice.changes.description": 2, + "FetchInvoice.changes.description_appended": 1, + "FetchInvoice.changes.vendor": 4, + "FetchInvoice.changes.vendor_removed": 3 + }, + "FetchinvoiceNext_period": { + "FetchInvoice.next_period.counter": 1, + "FetchInvoice.next_period.endtime": 3, + "FetchInvoice.next_period.paywindow_end": 5, + "FetchInvoice.next_period.paywindow_start": 4, + "FetchInvoice.next_period.starttime": 2 + }, + "FetchinvoiceRequest": { + "FetchInvoice.amount_msat": 2, + "FetchInvoice.offer": 1, + "FetchInvoice.payer_note": 8, + "FetchInvoice.quantity": 3, + "FetchInvoice.recurrence_counter": 4, + "FetchInvoice.recurrence_label": 6, + "FetchInvoice.recurrence_start": 5, + "FetchInvoice.timeout": 7 + }, + "FetchinvoiceResponse": { + "FetchInvoice.changes": 2, + "FetchInvoice.invoice": 1, + "FetchInvoice.next_period": 3 + }, "FundchannelRequest": { "FundChannel.amount": 1, "FundChannel.announce": 3, @@ -1603,6 +1642,17 @@ "UtxoPsbt.psbt": 1, "UtxoPsbt.reservations[]": 6 }, + "WaitRequest": { + "Wait.indexname": 2, + "Wait.nextvalue": 3, + "Wait.subsystem": 1 + }, + "WaitResponse": { + "Wait.created": 2, + "Wait.deleted": 4, + "Wait.subsystem": 1, + "Wait.updated": 3 + }, "WaitanyinvoicePaid_outpoint": { "WaitAnyInvoice.paid_outpoint.outnum": 2, "WaitAnyInvoice.paid_outpoint.txid": 1 @@ -2853,6 +2903,94 @@ "added": "pre-v0.10.1", "deprecated": false }, + "FetchInvoice": { + "added": "pre-v0.10.1", + "deprecated": null + }, + "FetchInvoice.amount_msat": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.changes": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.changes.amount_msat": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.changes.description": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.changes.description_appended": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.changes.vendor": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.changes.vendor_removed": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.invoice": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.next_period": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.next_period.counter": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.next_period.endtime": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.next_period.paywindow_end": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.next_period.paywindow_start": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.next_period.starttime": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.offer": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.payer_note": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.quantity": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.recurrence_counter": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.recurrence_label": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.recurrence_start": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "FetchInvoice.timeout": { + "added": "pre-v0.10.1", + "deprecated": false + }, "FundChannel": { "added": "pre-v0.10.1", "deprecated": null @@ -5613,6 +5751,34 @@ "added": "pre-v0.10.1", "deprecated": false }, + "Wait": { + "added": "v23.08", + "deprecated": null + }, + "Wait.created": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "Wait.deleted": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "Wait.indexname": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "Wait.nextvalue": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "Wait.subsystem": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "Wait.updated": { + "added": "pre-v0.10.1", + "deprecated": false + }, "WaitAnyInvoice": { "added": "pre-v0.10.1", "deprecated": null diff --git a/Makefile b/Makefile index 7e3f4fb68a99..7ce942db0cbb 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,7 @@ COMPAT_CFLAGS=-DCOMPAT_V052=1 -DCOMPAT_V060=1 -DCOMPAT_V061=1 -DCOMPAT_V062=1 -D endif # (method=thread to support xdist) -PYTEST_OPTS := -v -p no:logging $(PYTEST_OPTS) +PYTEST_OPTS := -v -p no:logging $(PYTEST_OPTS) $(PYTEST_MOREOPTS) MY_CHECK_PYTHONPATH=$${PYTHONPATH}$${PYTHONPATH:+:}$(shell pwd)/contrib/pyln-client:$(shell pwd)/contrib/pyln-testing:$(shell pwd)/contrib/pyln-proto/:$(shell pwd)/external/lnprototest:$(shell pwd)/contrib/pyln-spec/bolt1:$(shell pwd)/contrib/pyln-spec/bolt2:$(shell pwd)/contrib/pyln-spec/bolt4:$(shell pwd)/contrib/pyln-spec/bolt7 # Collect generated python files to be excluded from lint checks PYTHON_GENERATED= \ @@ -718,7 +718,9 @@ clean: obsclean PYLNS=client proto testing -# See doc/MAKING-RELEASES.md +# See doc/contribute-to-core-lightning/contributor-workflow.md +update-py-versions: update-pyln-versions update-clnrest-version update-poetry-lock + update-pyln-versions: $(PYLNS:%=update-pyln-version-%) update-pyln-version-%: @@ -730,6 +732,13 @@ pyln-release: $(PYLNS:%=pyln-release-%) pyln-release-%: cd contrib/pyln-$* && $(MAKE) prod-release +update-clnrest-version: + @if [ -z "$(NEW_VERSION)" ]; then echo "Set NEW_VERSION!" >&2; exit 1; fi + cd plugins/clnrest && $(MAKE) upgrade-version + +update-poetry-lock: + poetry update clnrest pyln-client pyln-proto pyln-testing + update-mocks: $(ALL_TEST_PROGRAMS:%=update-mocks/%.c) $(ALL_TEST_PROGRAMS:%=update-mocks/%.c): $(ALL_GEN_HEADERS) $(EXTERNAL_LIBS) libccan.a ccan/ccan/cdump/tools/cdump-enumstr config.vars diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index 34153e94a397..42a89bfb8ffd 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -290,6 +290,27 @@ bool psbt_input_set_signature(struct wally_psbt *psbt, size_t in, return ok; } +bool psbt_input_have_signature(const struct wally_psbt *psbt, + size_t in, + const struct pubkey *pubkey, + bool *signature_found) +{ + u8 pk_der[PUBKEY_CMPR_LEN]; + size_t index_plus_one; + bool ok; + + assert(in < psbt->num_inputs); + + pubkey_to_der(pk_der, pubkey); + + ok = wally_psbt_input_find_signature(&psbt->inputs[in], pk_der, + sizeof(pk_der), + &index_plus_one) == WALLY_OK; + if (ok) + *signature_found = index_plus_one > 0; + return ok; +} + void psbt_input_set_wit_utxo(struct wally_psbt *psbt, size_t in, const u8 *scriptPubkey, struct amount_sat amt) { diff --git a/bitcoin/psbt.h b/bitcoin/psbt.h index 49682b09ebba..4c693c8a3636 100644 --- a/bitcoin/psbt.h +++ b/bitcoin/psbt.h @@ -178,6 +178,14 @@ WARN_UNUSED_RESULT bool psbt_input_set_signature(struct wally_psbt *psbt, size_t const struct pubkey *pubkey, const struct bitcoin_signature *sig); +/* Returns false on error. On success, *signature_found is set to true if the + * input has a signature present for `pubkey` and false if if one was not found. + * Only ignature presence is checked, is not validated. */ +WARN_UNUSED_RESULT bool psbt_input_have_signature(const struct wally_psbt *psbt, + size_t in, + const struct pubkey *pubkey, + bool *signature_found); + void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript); /* psbt_input_set_unknown - Set the given Key-Value in the psbt's input keymap diff --git a/channeld/channeld.c b/channeld/channeld.c index e91da4688766..a4ea9219c744 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -90,7 +90,8 @@ struct peer { struct pubkey remote_per_commit; /* Remotes's last per-commitment point: we keep this to check - * revoke_and_ack's `per_commitment_secret` is correct. */ + * revoke_and_ack's `per_commitment_secret` is correct and for + * splices. */ struct pubkey old_remote_per_commit; /* Their sig for current commit. */ @@ -786,14 +787,12 @@ static void check_mutual_splice_locked(struct peer *peer) /* We must regossip the scid since it has changed */ peer->gossip_scid_announced = false; - channel_announcement_negotiate(peer); + if (channel_announcement_negotiate(peer)) + send_channel_update(peer, true); billboard_update(peer); - send_channel_update(peer, true); 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`. @@ -815,6 +814,13 @@ static void handle_peer_splice_locked(struct peer *peer, const u8 *msg) type_to_string(msg, struct channel_id, &peer->channel_id)); + /* If we've `mutual_splice_locked` but our peer hasn't, we can ignore + * this message harmlessly */ + if (!tal_count(peer->splice_state->inflights)) { + status_info("Peer sent redundant splice_locked, ignoring"); + return; + } + peer->splice_state->locked_ready[REMOTE] = true; check_mutual_splice_locked(peer); } @@ -906,13 +912,15 @@ static void handle_peer_announcement_signatures(struct peer *peer, const u8 *msg * `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])) + &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]), + &peer->short_channel_ids[LOCAL]), type_to_string(tmpctx, struct short_channel_id, - &peer->short_channel_ids[LOCAL])); + &remote_scid)); + return; + } peer->short_channel_ids[REMOTE] = remote_scid; @@ -1315,6 +1323,7 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, const u8 *funding_wscript, const struct htlc **htlc_map, u64 commit_index, + const struct pubkey *remote_per_commit, struct bitcoin_signature *commit_sig) { struct simple_htlc **htlcs; @@ -1326,7 +1335,7 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, htlcs = collect_htlcs(tmpctx, htlc_map); msg = towire_hsmd_sign_remote_commitment_tx(NULL, txs[0], &peer->channel->funding_pubkey[REMOTE], - &peer->remote_per_commit, + remote_per_commit, channel_has(peer->channel, OPT_STATIC_REMOTEKEY), commit_index, @@ -1350,7 +1359,7 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, dump_htlcs(peer->channel, "Sending commit_sig"); if (!derive_simple_key(&peer->channel->basepoints[LOCAL].htlc, - &peer->remote_per_commit, + remote_per_commit, &local_htlckey)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Deriving local_htlckey"); @@ -1370,7 +1379,7 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, wscript = bitcoin_tx_output_get_witscript(tmpctx, txs[0], txs[i+1]->wtx->inputs[0].index); msg = towire_hsmd_sign_remote_htlc_tx(NULL, txs[i + 1], wscript, - &peer->remote_per_commit, + remote_per_commit, channel_has_anchors(peer->channel)); msg = hsm_req(tmpctx, take(msg)); @@ -1511,6 +1520,7 @@ static u8 *send_commit_part(const tal_t *ctx, s64 splice_amnt, s64 remote_splice_amnt, u64 remote_index, + const struct pubkey *remote_per_commit, struct local_anchor_info **anchor) { u8 *msg; @@ -1539,12 +1549,12 @@ static u8 *send_commit_part(const tal_t *ctx, txs = channel_txs(tmpctx, funding, funding_sats, &htlc_map, direct_outputs, &funding_wscript, - peer->channel, &peer->remote_per_commit, + peer->channel, remote_per_commit, remote_index, REMOTE, splice_amnt, remote_splice_amnt, &local_anchor_outnum); htlc_sigs = calc_commitsigs(tmpctx, peer, txs, funding_wscript, htlc_map, - remote_index, &commit_sig); + remote_index, remote_per_commit, &commit_sig); if (direct_outputs[LOCAL] != NULL) { pbase = penalty_base_new(tmpctx, remote_index, @@ -1702,9 +1712,7 @@ static void send_commit(struct peer *peer) */ changed_htlcs = tal_arr(tmpctx, const struct htlc *, 0); - if (peer->splice_state->committed_count == peer->splice_state->count - && !channel_sending_commit(peer->channel, &changed_htlcs)) { - + if (!channel_sending_commit(peer->channel, &changed_htlcs)) { status_debug("Can't send commit: nothing to send," " feechange %s (%s)" " blockheight %s (%s)", @@ -1724,7 +1732,7 @@ static void send_commit(struct peer *peer) msgs[0] = send_commit_part(msgs, peer, &peer->channel->funding, peer->channel->funding_sats, changed_htlcs, true, 0, 0, peer->next_index[REMOTE], - &local_anchor); + &peer->remote_per_commit, &local_anchor); if (local_anchor) tal_arr_expand(&anchors_info, *local_anchor); @@ -1751,6 +1759,7 @@ static void send_commit(struct peer *peer) peer->splice_state->inflights[i]->splice_amnt, remote_splice_amnt, peer->next_index[REMOTE], + &peer->remote_per_commit, &local_anchor)); if (local_anchor) tal_arr_expand(&anchors_info, *local_anchor); @@ -1766,8 +1775,6 @@ static void send_commit(struct peer *peer) 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); /* Timer now considered expired, you can add a new one. */ @@ -1973,13 +1980,20 @@ struct commitsig_info { * consecutive commitment messages equal to the number of inflight splices. * * Returns the last commitsig received. When splicing this is the - * newest splice commit sig. */ + * newest splice commit sig. + * + * `commit_index` 0 refers to the funding commit. `commit_index` 1 and above + * refer to inflight splices. + */ static struct commitsig_info *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) + const u8 *msg, + u32 commit_index, + const struct htlc **changed_htlcs, + s64 splice_amnt, + s64 remote_splice_amnt, + u64 local_index, + const struct pubkey *local_per_commit, + bool allow_empty_commit) { struct commitsig_info *result; struct channel_id channel_id; @@ -2040,7 +2054,7 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, 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) { + && !allow_empty_commit) { /* BOLT #2: * * A sending node: @@ -2082,8 +2096,8 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, txs = channel_txs(tmpctx, &outpoint, funding_sats, &htlc_map, NULL, &funding_wscript, peer->channel, - &peer->next_local_per_commit, - peer->next_index[LOCAL], LOCAL, splice_amnt, + local_per_commit, + local_index, LOCAL, splice_amnt, remote_splice_amnt, &remote_anchor_outnum); /* Set the commit_sig on the commitment tx psbt */ @@ -2094,7 +2108,7 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, "Unable to set signature internally"); if (!derive_simple_key(&peer->channel->basepoints[REMOTE].htlc, - &peer->next_local_per_commit, &remote_htlckey)) + local_per_commit, &remote_htlckey)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Deriving remote_htlckey"); status_debug("Derived key %s from basepoint %s, point %s", @@ -2102,7 +2116,7 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, type_to_string(tmpctx, struct pubkey, &peer->channel->basepoints[REMOTE].htlc), type_to_string(tmpctx, struct pubkey, - &peer->next_local_per_commit)); + local_per_commit)); /* BOLT #2: * * A receiving node: @@ -2120,7 +2134,7 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, " %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], + local_index, type_to_string(msg, struct bitcoin_signature, &commit_sig), type_to_string(msg, struct bitcoin_tx, txs[0]), @@ -2131,9 +2145,11 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, 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)), + cs_tlv && cs_tlv->splice_info + ? type_to_string(tmpctx, + struct channel_id, + cs_tlv->splice_info) + : "N/A", peer->splice_state->await_commitment_succcess ? "yes" : "no", tal_count(peer->splice_state->inflights)); @@ -2198,7 +2214,7 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, msg2 = towire_hsmd_validate_commitment_tx(NULL, txs[0], (const struct simple_htlc **) htlcs, - peer->next_index[LOCAL], + local_index, channel_feerate(peer->channel, LOCAL), &commit_sig, htlc_sigs); @@ -2243,17 +2259,18 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, "WIRE_COMMITMENT_SIGNED but got %s", peer_wire_name(type)); - /* We purposely just store the last commit msg */ + /* We purposely just store the last commit msg in result */ result = handle_peer_commit_sig(peer, splice_msg, i + 1, changed_htlcs, sub_splice_amnt, - funding_diff - sub_splice_amnt); + funding_diff - sub_splice_amnt, + local_index, local_per_commit, + allow_empty_commit); old_secret = result->old_secret; tal_arr_expand(&commitsigs, result->commitsig); tal_steal(commitsigs, result); } assert(old_secret); - peer->splice_state->revoked_count = peer->splice_state->count; send_revocation(peer, &commit_sig, htlc_sigs, changed_htlcs, txs[0], old_secret, &next_point, commitsigs); @@ -2782,13 +2799,13 @@ static void add_amount_to_side(struct peer *peer, } static bool do_i_sign_first(struct peer *peer, struct wally_psbt *psbt, - enum tx_role our_role) + enum tx_role our_role, bool force_sign_first) { struct amount_msat in[NUM_TX_ROLES]; /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: * - MAY send `tx_signatures` first. */ - if (peer->splicing->force_sign_first) + if (force_sign_first) return true; in[TX_INITIATOR] = AMOUNT_MSAT(0); @@ -2824,17 +2841,19 @@ static struct wally_psbt *next_splice_step(const tal_t *ctx, 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) +static const u8 *peer_expect_msg_three(const tal_t *ctx, + struct peer *peer, + enum peer_wire expect_type, + enum peer_wire second_allowed_type, + enum peer_wire third_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) + if (type != expect_type && type != second_allowed_type + && type != third_allowed_type) peer_failed_warn(peer->pps, &peer->channel_id, "Got incorrect message from peer: %s" " (should be %s) [%s]", @@ -2847,63 +2866,100 @@ static const u8 *peer_expect_msg(const tal_t *ctx, /* 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. */ + * required commitments as part of the splicing process. + * If the first message received is `tx_abort` or `tx_signatures, NULL is + * returned. */ static struct commitsig *interactive_send_commitments(struct peer *peer, struct wally_psbt *psbt, - enum tx_role our_role) + enum tx_role our_role, + size_t inflight_index, + bool send_commitments, + bool recv_commitments, + const u8 **msg_received) { struct commitsig_info *result; - const u8 *msg, *commit_msg; + const u8 *msg; + struct pubkey my_current_per_commitment_point; + struct inflight *inflight = peer->splice_state->inflights[inflight_index]; + s64 funding_diff = sats_diff(inflight->amnt, + peer->channel->funding_sats); + s64 remote_splice_amnt = funding_diff - inflight->splice_amnt; + struct local_anchor_info *local_anchor; + u64 next_index_local = peer->next_index[LOCAL]; + u64 next_index_remote = peer->next_index[REMOTE]; - commit_msg = NULL; + if(msg_received) + *msg_received = NULL; - if (do_i_sign_first(peer, psbt, our_role)) { + if (do_i_sign_first(peer, psbt, our_role, inflight->force_sign_first) + && send_commitments) { status_debug("Splice %s: we commit first", our_role == TX_INITIATOR ? "initiator" : "accepter"); - send_commit(peer); + peer_write(peer->pps, send_commit_part(tmpctx, + peer, + &inflight->outpoint, + inflight->amnt, + NULL, false, + inflight->splice_amnt, + remote_splice_amnt, + next_index_remote - 1, + &peer->old_remote_per_commit, + &local_anchor)); + } - /* If both sides commit simultaneously, that's fine. */ - msg = peer_expect_msg(tmpctx, peer, WIRE_REVOKE_AND_ACK, - WIRE_COMMITMENT_SIGNED); + result = NULL; - /* 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); + if (recv_commitments) { + msg = peer_expect_msg_three(tmpctx, peer, + WIRE_COMMITMENT_SIGNED, + WIRE_TX_SIGNATURES, + WIRE_TX_ABORT); - msg = peer_expect_msg(tmpctx, peer, - WIRE_REVOKE_AND_ACK, 0); - } - - handle_peer_revoke_and_ack(peer, msg); - } + if (msg_received) + *msg_received = 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); + /* Funding counts as 0th commit so we do inflight_index + 1 */ + if (fromwire_peektype(msg) == WIRE_COMMITMENT_SIGNED) { + get_per_commitment_point(next_index_local - 1, + &my_current_per_commitment_point, NULL); + + result = handle_peer_commit_sig(peer, msg, + inflight_index + 1, + NULL, + inflight->splice_amnt, + remote_splice_amnt, + next_index_local - 1, + &my_current_per_commitment_point, + true); + } } - if (!do_i_sign_first(peer, psbt, our_role)) { + if (!do_i_sign_first(peer, psbt, our_role, inflight->force_sign_first) + && send_commitments) { 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); + peer_write(peer->pps, send_commit_part(tmpctx, + peer, + &inflight->outpoint, + inflight->amnt, + NULL, false, + inflight->splice_amnt, + remote_splice_amnt, + next_index_remote - 1, + &peer->old_remote_per_commit, + &local_anchor)); } - return result->commitsig; + /* Sending and receiving splice commit should not increment commit + * related indices */ + assert(next_index_local == peer->next_index[LOCAL]); + assert(next_index_remote == peer->next_index[REMOTE]); + + return result ? result->commitsig : NULL; } static struct wally_psbt_output *find_channel_output(struct peer *peer, @@ -2917,7 +2973,7 @@ static struct wally_psbt_output *find_channel_output(struct peer *peer, &peer->channel->funding_pubkey[LOCAL], &peer->channel->funding_pubkey[REMOTE]); - scriptpubkey = scriptpubkey_p2wsh(psbt, wit_script); + scriptpubkey = scriptpubkey_p2wsh(tmpctx, wit_script); for (size_t i = 0; i < psbt->num_outputs; i++) { if (memeq(psbt->outputs[i].script, @@ -3323,12 +3379,69 @@ static void update_view_from_inflights(struct peer *peer) } } -/* Called to finish an ongoing splice OR on restart from chanenl_reestablish */ +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; +} + +static size_t last_inflight_index(struct peer *peer) +{ + assert(tal_count(peer->splice_state->inflights) > 0); + + return tal_count(peer->splice_state->inflights) - 1; +} + +static bool have_i_signed_inflight(const struct peer *peer, + const struct inflight *inflight) +{ + bool has_sig; + u32 index; + + index = find_channel_funding_input(inflight->psbt, + &peer->channel->funding); + + if (!psbt_input_have_signature(inflight->psbt, index, + &peer->channel->funding_pubkey[LOCAL], + &has_sig)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable parse inflight psbt"); + + return has_sig; +} + +static bool check_tx_abort(struct peer *peer, const u8 *msg) +{ + if (!msg || fromwire_peektype(msg) != WIRE_TX_ABORT) + return false; + + if (have_i_signed_inflight(peer, last_inflight(peer))) { + peer_failed_err(peer->pps, &peer->channel_id, "tx_abort" + " is not allowed after I have sent my" + " signature. msg: %s", + tal_hex(tmpctx, msg)); + } + + /* DTODO: Remove last_inflight */ + + return true; +} + +/* Called to finish an ongoing splice OR on restart from chanenl_reestablish. */ static void resume_splice_negotiation(struct peer *peer, - struct inflight *inflight, - bool skip_commitments, - enum tx_role our_role) + bool send_commitments, + bool recv_commitments, + bool send_signature, + bool recv_signature) { + struct inflight *inflight = last_inflight(peer); + enum tx_role our_role = inflight->i_am_initiator + ? TX_INITIATOR + : TX_ACCEPTER; const u8 *wit_script; struct channel_id cid; enum peer_wire type; @@ -3346,8 +3459,17 @@ static void resume_splice_negotiation(struct peer *peer, struct bitcoin_signature their_sig; struct pubkey *their_pubkey; struct bitcoin_tx *final_tx; + struct bitcoin_txid final_txid; u8 **wit_stack; struct tlv_txsigs_tlvs *txsig_tlvs, *their_txsigs_tlvs; + const u8 *msg_received; + + status_info("Splice negotation, will %ssend commit, %srecv commit," + " %ssend signature, %srecv signature", + send_commitments ? "" : "not ", + recv_commitments ? "" : "not ", + send_signature ? "" : "not ", + recv_signature ? "" : "not "); wit_script = bitcoin_redeem_2of2(tmpctx, &peer->channel->funding_pubkey[LOCAL], @@ -3358,10 +3480,20 @@ static void resume_splice_negotiation(struct peer *peer, 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); + msg_received = NULL; + their_commit = interactive_send_commitments(peer, current_psbt, + our_role, + last_inflight_index(peer), + send_commitments, + recv_commitments, + &msg_received); + + if (check_tx_abort(peer, msg_received)) + return; + if (their_commit) { + if (inflight->last_tx != their_commit->tx) + inflight->last_tx = tal_free(inflight->last_tx); inflight->last_tx = tal_steal(inflight, their_commit->tx); inflight->last_sig = their_commit->commit_signature; @@ -3371,6 +3503,12 @@ static void resume_splice_negotiation(struct peer *peer, wire_sync_write(MASTER_FD, take(msg)); } + if (!inflight->last_tx) + peer_failed_err(peer->pps, &peer->channel_id, + "Splice needs commitment signature to continue" + " but your last msg was %s", + msg_received ? tal_hex(tmpctx, msg_received) : "NULL"); + /* DTODO Validate splice tx takes none of our funds in either: * 1) channel balance * 2) other side sneakily adding other outputs we own @@ -3407,155 +3545,198 @@ static void resume_splice_negotiation(struct peer *peer, "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)); - - + 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); + /* DTODO: is this finalize call required? */ + psbt_finalize(current_psbt); + 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)) { + psbt_txid(tmpctx, current_psbt, &final_txid, NULL); + + if (do_i_sign_first(peer, current_psbt, our_role, + inflight->force_sign_first) + && send_signature) { msg = towire_channeld_update_inflight(NULL, current_psbt, NULL, NULL); wire_sync_write(MASTER_FD, take(msg)); + + msg = towire_channeld_splice_sending_sigs(tmpctx, &final_txid); + wire_sync_write(MASTER_FD, take(msg)); + peer_write(peer->pps, sigmsg); - status_debug("Splice: we signed first"); } - msg = peer_read(tmpctx, peer->pps); + their_pubkey = &peer->channel->funding_pubkey[REMOTE]; - type = fromwire_peektype(msg); + if (recv_signature) { + if (fromwire_peektype(msg_received) == WIRE_TX_SIGNATURES) + msg = msg_received; + else + msg = peer_read(tmpctx, peer->pps); - if (handle_peer_error_or_warning(peer->pps, msg)) - return; + type = fromwire_peektype(msg); - 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)); + if (check_tx_abort(peer, msg)) + return; - /* 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); + if (handle_peer_error_or_warning(peer->pps, msg)) + return; - /* BOLT-a8b9f495cac28124c69cc5ee429f9ef2bacb9921 #2: - * Both nodes: - * - MUST sign the transaction using SIGHASH_ALL */ - their_sig.sighash_type = SIGHASH_ALL; + 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)); - if (!signature_from_der(their_txsigs_tlvs->funding_outpoint_sig, - tal_count(their_txsigs_tlvs->funding_outpoint_sig), - &their_sig)) { + 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)); - 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); - their_pubkey = &peer->channel->funding_pubkey[REMOTE]; + /* BOLT-a8b9f495cac28124c69cc5ee429f9ef2bacb9921 #2: + * Both nodes: + * - MUST sign the transaction using SIGHASH_ALL */ + their_sig.sighash_type = SIGHASH_ALL; - /* Set the commit_sig on the commitment tx psbt */ - if (!psbt_input_set_signature(current_psbt, - splice_funding_index, - their_pubkey, - &their_sig)) { + if (!signature_from_der(their_txsigs_tlvs->funding_outpoint_sig, + tal_count(their_txsigs_tlvs->funding_outpoint_sig), + &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)); - } + peer_failed_warn(peer->pps, &peer->channel_id, + "Splicing bad tx_signatures %s", + tal_hex(msg, msg)); + } - psbt_input_set_witscript(current_psbt, - splice_funding_index, - wit_script); + /* Set the commit_sig on the commitment tx psbt */ + if (!psbt_input_set_signature(current_psbt, + splice_funding_index, + their_pubkey, + &their_sig)) { - if (tal_count(inws) > current_psbt->num_inputs) - peer_failed_warn(peer->pps, &peer->channel_id, - "%zu 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; + 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)); } - if (in_serial % 2 == our_role) - continue; - if (i == splice_funding_index) - continue; + psbt_input_set_witscript(current_psbt, + splice_funding_index, + wit_script); - if (j == tal_count(inws)) - peer_failed_warn(peer->pps, - &peer->channel_id, - "Mismatch witness stack count %s", - tal_hex(msg, msg)); + if (tal_count(inws) > current_psbt->num_inputs) + peer_failed_warn(peer->pps, &peer->channel_id, + "%zu 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; - psbt_finalize_input(current_psbt, in, inws[j++]); - } + if (i == splice_funding_index) + continue; - final_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt); + if (j == tal_count(inws)) + peer_failed_warn(peer->pps, + &peer->channel_id, + "Mismatch witness stack count." + " Most likely you are missing" + " signatures. Your" + " TX_SIGNATURES message: %s.", + tal_hex(msg, msg)); - wit_stack = bitcoin_witness_2of2(current_psbt, &splice_sig, &their_sig, - &peer->channel->funding_pubkey[LOCAL], - their_pubkey); + psbt_finalize_input(current_psbt, in, inws[j++]); + } - bitcoin_tx_input_set_witness(final_tx, splice_funding_index, wit_stack); + final_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt); - /* We let core validate our peer's signatures are correct. */ + wit_stack = bitcoin_witness_2of2(final_tx, &splice_sig, + &their_sig, + &peer->channel->funding_pubkey[LOCAL], + their_pubkey); - msg = towire_channeld_update_inflight(NULL, current_psbt, NULL, NULL); - wire_sync_write(MASTER_FD, take(msg)); + 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, + inflight->force_sign_first) + && send_signature) { + msg = towire_channeld_splice_sending_sigs(tmpctx, &final_txid); + wire_sync_write(MASTER_FD, take(msg)); - if (!do_i_sign_first(peer, current_psbt, our_role)) { peer_write(peer->pps, sigmsg); status_debug("Splice: we signed second"); } peer->splicing = tal_free(peer->splicing); - msg = towire_channeld_splice_confirmed_signed(tmpctx, final_tx, - chan_output_index); - wire_sync_write(MASTER_FD, take(msg)); + if (recv_signature) { + msg = towire_channeld_splice_confirmed_signed(tmpctx, final_tx, + chan_output_index); + wire_sync_write(MASTER_FD, take(msg)); - send_channel_update(peer, true); + send_channel_update(peer, true); + } } static struct inflight *inflights_new(struct peer *peer) @@ -3725,7 +3906,8 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg) both_amount, peer->splicing->accepter_relative, ictx->current_psbt, - false); + false, + peer->splicing->force_sign_first); master_wait_sync_reply(tmpctx, peer, take(msg), WIRE_CHANNELD_GOT_INFLIGHT); @@ -3740,6 +3922,7 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg) new_inflight->splice_amnt = peer->splicing->accepter_relative; new_inflight->last_tx = NULL; new_inflight->i_am_initiator = false; + new_inflight->force_sign_first = peer->splicing->force_sign_first; current_push_val = relative_splice_balance_fundee(peer, our_role,ictx->current_psbt, outpoint.n, splice_funding_index); @@ -3749,7 +3932,7 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg) peer->splice_state->count++; - resume_splice_negotiation(peer, new_inflight, false, TX_ACCEPTER); + resume_splice_negotiation(peer, true, true, true, true); } static struct bitcoin_tx *bitcoin_tx_from_txid(struct peer *peer, @@ -3961,7 +4144,8 @@ static void splice_initiator_user_finalized(struct peer *peer) amount_sat(new_chan_output->amount), peer->splicing->opener_relative, ictx->current_psbt, - true); + true, + peer->splicing->force_sign_first); master_wait_sync_reply(tmpctx, peer, take(outmsg), WIRE_CHANNELD_GOT_INFLIGHT); @@ -3975,6 +4159,7 @@ static void splice_initiator_user_finalized(struct peer *peer) new_inflight->splice_amnt = peer->splicing->opener_relative; new_inflight->last_tx = NULL; new_inflight->i_am_initiator = true; + new_inflight->force_sign_first = peer->splicing->force_sign_first; current_push_val = relative_splice_balance_fundee(peer, our_role, ictx->current_psbt, chan_output_index, splice_funding_index); @@ -3985,7 +4170,9 @@ static void splice_initiator_user_finalized(struct peer *peer) peer->splice_state->count++; their_commit = interactive_send_commitments(peer, ictx->current_psbt, - our_role); + our_role, + last_inflight_index(peer), + true, true, NULL); new_inflight->last_tx = tal_steal(new_inflight, their_commit->tx); new_inflight->last_sig = their_commit->commit_signature; @@ -4077,16 +4264,6 @@ static void splice_initiator_user_update(struct peer *peer, const u8 *inmsg) 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`. @@ -4099,7 +4276,7 @@ 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; + const u8 *msg, *outmsg; if (!peer->splicing) { msg = towire_channeld_splice_state_error(NULL, "Can't accept a" @@ -4156,7 +4333,15 @@ static void splice_initiator_user_signed(struct peer *peer, const u8 *inmsg) inflight = last_inflight(peer); inflight->psbt = tal_steal(inflight, signed_psbt); - resume_splice_negotiation(peer, inflight, true, TX_INITIATOR); + /* Save the user provided signatures to DB incase we have to + * restart and reestablish later. */ + outmsg = towire_channeld_update_inflight(NULL, inflight->psbt, + inflight->last_tx, + &inflight->last_sig); + + wire_sync_write(MASTER_FD, take(outmsg)); + + resume_splice_negotiation(peer, false, false, true, true); } /* This occurs once our 'stfu' transition was successful. */ @@ -4192,7 +4377,7 @@ static void handle_splice_init(struct peer *peer, const u8 *inmsg) peer->splicing = splicing_new(peer); - if (!fromwire_channeld_splice_init(peer, inmsg, + if (!fromwire_channeld_splice_init(peer->splicing, inmsg, &peer->splicing->current_psbt, &peer->splicing->opener_relative, &peer->splicing->feerate_per_kw, @@ -4285,7 +4470,9 @@ static void peer_in(struct peer *peer, const u8 *msg) handle_peer_add_htlc(peer, msg); return; case WIRE_COMMITMENT_SIGNED: - handle_peer_commit_sig(peer, msg, 0, NULL, 0, 0); + handle_peer_commit_sig(peer, msg, 0, NULL, 0, 0, + peer->next_index[LOCAL], + &peer->next_local_per_commit, false); return; case WIRE_UPDATE_FEE: handle_peer_feechange(peer, msg); @@ -4515,6 +4702,7 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) msgs[0] = send_commit_part(msgs, peer, &peer->channel->funding, peer->channel->funding_sats, NULL, false, 0, 0, peer->next_index[REMOTE] - 1, + &peer->remote_per_commit, &local_anchor); /* Loop over current inflights @@ -4540,6 +4728,7 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) peer->splice_state->inflights[i]->splice_amnt, remote_splice_amnt, peer->next_index[REMOTE] - 1, + &peer->remote_per_commit, &local_anchor)); } @@ -4805,22 +4994,12 @@ static void peer_reconnect(struct peer *peer, get_per_commitment_point(peer->next_index[LOCAL] - 1, &my_current_per_commitment_point, NULL); - inflight = last_inflight(peer); + send_tlvs = NULL; if (peer->experimental_upgrade) { /* Subtle: we free tmpctx below as we loop, so tal off peer */ send_tlvs = tlv_channel_reestablish_tlvs_new(peer); - /* If inflight with no sigs on it, send next_funding */ - if (inflight && !inflight->last_tx) { - status_debug("Reestablish with an inflight but missing" - " last_tx, will send next_funding %s", - type_to_string(tmpctx, - struct bitcoin_txid, - &inflight->outpoint.txid)); - 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 @@ -4854,8 +5033,23 @@ static void peer_reconnect(struct peer *peer, take(channel_upgradable_type(NULL, peer->channel))); } - } else - send_tlvs = NULL; + } + + inflight = last_inflight(peer); + + if (inflight && (!inflight->last_tx || !inflight->remote_tx_sigs)) { + status_info("Reconnecting to peer with pending inflight commit:" + " %s, remote sigs: %s.", + inflight->last_tx ? "received" : "missing", + inflight->remote_tx_sigs ? "received" : "missing"); + + if (!send_tlvs) { + /* Subtle: we free tmpctx below as we loop, so tal off + * peer */ + send_tlvs = tlv_channel_reestablish_tlvs_new(peer); + } + send_tlvs->next_funding = &inflight->outpoint.txid; + } /* BOLT #2: * @@ -4959,6 +5153,84 @@ static void peer_reconnect(struct peer *peer, tal_count(peer->splice_state->inflights), peer->splice_state->count); + /* If we didn't send (i.e. don't support!) ignore theirs */ + if (!send_tlvs && !inflight) + recv_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); + + local_next_funding = (send_tlvs ? send_tlvs->next_funding : NULL); + remote_next_funding = (recv_tlvs ? recv_tlvs->next_funding : NULL); + + status_info("Splice resume check with local_next_funding: %s," + " remote_next_funding: %s, inflights: %zu", + local_next_funding ? "sent" : "omitted", + remote_next_funding ? "received" : "empty", + tal_count(peer->splice_state->inflights)); + + /* DTODO: Update splice BOLT spec PR and reference here. */ + if (inflight && (remote_next_funding || local_next_funding)) { + if (!remote_next_funding) { + status_info("Resuming splice negotation."); + resume_splice_negotiation(peer, + false, + true, + false, + true); + } else if (bitcoin_txid_eq(remote_next_funding, + &inflight->outpoint.txid)) { + status_info("Resuming splice negotation"); + resume_splice_negotiation(peer, + !inflight->remote_tx_sigs, + local_next_funding, + true, + local_next_funding); + } else if (bitcoin_txid_eq(remote_next_funding, + &peer->channel->funding.txid)) { + peer_failed_err(peer->pps, + &peer->channel_id, + "Invalid reestablish with next_funding" + " txid %s that matches our current" + " active funding txid %s. Should be %s" + " or NULL", + type_to_string(tmpctx, + struct bitcoin_txid, + remote_next_funding), + type_to_string(tmpctx, + struct bitcoin_txid, + &peer->channel->funding.txid), + type_to_string(tmpctx, + struct bitcoin_txid, + &inflight->outpoint.txid)); + } else { /* remote_next_funding set but unrecognized */ + peer_failed_err(peer->pps, + &peer->channel_id, + "Invalid reestablish with unrecognized" + " next_funding txid %s, should be %s", + type_to_string(tmpctx, + struct bitcoin_txid, + remote_next_funding), + type_to_string(tmpctx, + struct bitcoin_txid, + &inflight->outpoint.txid)); + } + } else if (remote_next_funding) { /* No current inflight */ + if (bitcoin_txid_eq(remote_next_funding, + &peer->channel->funding.txid)) { + status_info("We have no pending splice but peer" + " expects one; resending splice_lock"); + peer_write(peer->pps, + take(towire_splice_locked(NULL, &peer->channel_id))); + } + else { + char *errmsg = tal_fmt(tmpctx, + "next_funding_txid not recognized." + " Sending tx_abort."); + peer_write(peer->pps, + take(towire_tx_abort(NULL, + &peer->channel_id, + (u8*)errmsg))); + } + } + /* BOLT #2: * * - if `next_commitment_number` is 1 in both the @@ -5126,10 +5398,6 @@ static void peer_reconnect(struct peer *peer, /* (If we had sent `closing_signed`, we'd be in closingd). */ maybe_send_shutdown(peer); - /* If we didn't send (i.e. don't support!) ignore theirs */ - if (!send_tlvs) - recv_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); - if (recv_tlvs->desired_channel_type) status_debug("They sent desired_channel_type [%s]", fmt_featurebits(tmpctx, @@ -5221,58 +5489,6 @@ 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) { - bool next_matches_current = false, next_matches_inflight = false; - - 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 { - status_info("Resuming splice negotation"); - 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 @@ -5349,6 +5565,10 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) struct short_channel_id, &peer->splice_state->short_channel_id)); } else { + status_debug("handle_funding_depth: Setting short_channel_ids[LOCAL] to %s", + type_to_string(tmpctx, + struct short_channel_id, + (scid ? scid : alias_local))); /* 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 @@ -5395,7 +5615,8 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) peer->announce_depth_reached = (depth >= ANNOUNCE_MIN_DEPTH); /* Send temporary or final announcements */ - channel_announcement_negotiate(peer); + if (!splicing) + channel_announcement_negotiate(peer); } billboard_update(peer); @@ -5775,6 +5996,7 @@ static void req_in(struct peer *peer, const u8 *msg) return; case WIRE_CHANNELD_SPLICE_CONFIRMED_INIT: case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED: + case WIRE_CHANNELD_SPLICE_SENDING_SIGS: case WIRE_CHANNELD_SPLICE_CONFIRMED_UPDATE: case WIRE_CHANNELD_SPLICE_LOOKUP_TX: case WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT: @@ -5923,8 +6145,6 @@ static void init_channel(struct peer *peer) 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); status_debug("option_static_remotekey = %u," diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 1992f7fa12d4..53590a2e6ee6 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -243,6 +243,10 @@ msgtype,channeld_splice_confirmed_signed,7213 msgdata,channeld_splice_confirmed_signed,tx,bitcoin_tx, msgdata,channeld_splice_confirmed_signed,output_index,u32, +# channeld->master: Splice signatures are about to be sent +msgtype,channeld_splice_sending_sigs,7214 +msgdata,channeld_splice_sending_sigs,tx,bitcoin_txid, + # channeld->master: A feerate error has occured msgtype,channeld_splice_feerate_error,7215 msgdata,channeld_splice_feerate_error,fee,amount_msat, @@ -257,6 +261,7 @@ 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, +msgdata,channeld_add_inflight,force_sign_first,bool, # master->channeld: Inflight saved successfully msgtype,channeld_got_inflight,7217 diff --git a/channeld/inflight.c b/channeld/inflight.c index 3c7796960f72..a1e908ead6ab 100644 --- a/channeld/inflight.c +++ b/channeld/inflight.c @@ -10,6 +10,7 @@ struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t * fromwire_bitcoin_outpoint(cursor, max, &inflight->outpoint); inflight->amnt = fromwire_amount_sat(cursor, max); + inflight->remote_tx_sigs = fromwire_bool(cursor, max); inflight->psbt = fromwire_wally_psbt(inflight, cursor, max); inflight->splice_amnt = fromwire_s64(cursor, max); int has_tx = fromwire_u8(cursor, max); @@ -22,6 +23,7 @@ struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t * memset(&inflight->last_sig, 0, sizeof(inflight->last_sig)); } inflight->i_am_initiator = fromwire_bool(cursor, max); + inflight->force_sign_first = fromwire_bool(cursor, max); return inflight; } @@ -30,6 +32,7 @@ void towire_inflight(u8 **pptr, const struct inflight *inflight) { towire_bitcoin_outpoint(pptr, &inflight->outpoint); towire_amount_sat(pptr, inflight->amnt); + towire_bool(pptr, inflight->remote_tx_sigs); towire_wally_psbt(pptr, inflight->psbt); towire_s64(pptr, inflight->splice_amnt); towire_u8(pptr, inflight->last_tx ? 1 : 0); @@ -38,15 +41,18 @@ void towire_inflight(u8 **pptr, const struct inflight *inflight) towire_bitcoin_signature(pptr, &inflight->last_sig); } towire_bool(pptr, inflight->i_am_initiator); + towire_bool(pptr, inflight->force_sign_first); } void copy_inflight(struct inflight *dest, struct inflight *src) { dest->outpoint = src->outpoint; dest->amnt = src->amnt; + dest->remote_tx_sigs = src->remote_tx_sigs; 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; + dest->force_sign_first = src->force_sign_first; } diff --git a/channeld/inflight.h b/channeld/inflight.h index c95acf9af215..47deb316681d 100644 --- a/channeld/inflight.h +++ b/channeld/inflight.h @@ -8,12 +8,15 @@ struct inflight { struct bitcoin_outpoint outpoint; struct amount_sat amnt; + bool remote_tx_sigs; 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; + bool force_sign_first; + bool is_locked; }; struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max); diff --git a/channeld/splice.c b/channeld/splice.c index 656017574d31..473159614f09 100644 --- a/channeld/splice.c +++ b/channeld/splice.c @@ -6,8 +6,6 @@ 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; diff --git a/channeld/splice.h b/channeld/splice.h index 4f1785a011a1..267fddbac679 100644 --- a/channeld/splice.h +++ b/channeld/splice.h @@ -21,10 +21,6 @@ struct splice_state { 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; }; diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 7185bec60c8f..8d7ed3086d47 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -52,6 +52,7 @@ service Node { rpc Decode(DecodeRequest) returns (DecodeResponse) {} rpc Disconnect(DisconnectRequest) returns (DisconnectResponse) {} rpc Feerates(FeeratesRequest) returns (FeeratesResponse) {} + rpc FetchInvoice(FetchinvoiceRequest) returns (FetchinvoiceResponse) {} rpc FundChannel(FundchannelRequest) returns (FundchannelResponse) {} rpc GetRoute(GetrouteRequest) returns (GetrouteResponse) {} rpc ListForwards(ListforwardsRequest) returns (ListforwardsResponse) {} @@ -63,6 +64,7 @@ service Node { rpc SignInvoice(SigninvoiceRequest) returns (SigninvoiceResponse) {} rpc SignMessage(SignmessageRequest) returns (SignmessageResponse) {} rpc WaitBlockHeight(WaitblockheightRequest) returns (WaitblockheightResponse) {} + rpc Wait(WaitRequest) returns (WaitResponse) {} rpc Stop(StopRequest) returns (StopResponse) {} rpc PreApproveKeysend(PreapprovekeysendRequest) returns (PreapprovekeysendResponse) {} rpc PreApproveInvoice(PreapproveinvoiceRequest) returns (PreapproveinvoiceResponse) {} @@ -1566,6 +1568,39 @@ message FeeratesOnchain_fee_estimates { uint64 htlc_success_satoshis = 5; } +message FetchinvoiceRequest { + string offer = 1; + optional Amount amount_msat = 2; + optional uint64 quantity = 3; + optional uint64 recurrence_counter = 4; + optional double recurrence_start = 5; + optional string recurrence_label = 6; + optional double timeout = 7; + optional string payer_note = 8; +} + +message FetchinvoiceResponse { + string invoice = 1; + FetchinvoiceChanges changes = 2; + optional FetchinvoiceNext_period next_period = 3; +} + +message FetchinvoiceChanges { + optional string description_appended = 1; + optional string description = 2; + optional string vendor_removed = 3; + optional string vendor = 4; + optional Amount amount_msat = 5; +} + +message FetchinvoiceNext_period { + uint64 counter = 1; + uint64 starttime = 2; + uint64 endtime = 3; + uint64 paywindow_start = 4; + uint64 paywindow_end = 5; +} + message FundchannelRequest { bytes id = 9; AmountOrAll amount = 1; @@ -1805,6 +1840,37 @@ message WaitblockheightResponse { uint32 blockheight = 1; } +message WaitRequest { + // Wait.subsystem + enum WaitSubsystem { + INVOICES = 0; + FORWARDS = 1; + SENDPAYS = 2; + } + // Wait.indexname + enum WaitIndexname { + CREATED = 0; + UPDATED = 1; + DELETED = 2; + } + WaitSubsystem subsystem = 1; + WaitIndexname indexname = 2; + uint64 nextvalue = 3; +} + +message WaitResponse { + // Wait.subsystem + enum WaitSubsystem { + INVOICES = 0; + FORWARDS = 1; + SENDPAYS = 2; + } + WaitSubsystem subsystem = 1; + optional uint64 created = 2; + optional uint64 updated = 3; + optional uint64 deleted = 4; +} + message StopRequest { } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 0e8fcc40ab90..5f6693fcd22b 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1482,6 +1482,43 @@ impl From for pb::FeeratesResponse { } } +#[allow(unused_variables)] +impl From for pb::FetchinvoiceChanges { + fn from(c: responses::FetchinvoiceChanges) -> Self { + Self { + description_appended: c.description_appended, // Rule #2 for type string? + description: c.description, // Rule #2 for type string? + vendor_removed: c.vendor_removed, // Rule #2 for type string? + vendor: c.vendor, // Rule #2 for type string? + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + } + } +} + +#[allow(unused_variables)] +impl From for pb::FetchinvoiceNextPeriod { + fn from(c: responses::FetchinvoiceNext_period) -> Self { + Self { + counter: c.counter, // Rule #2 for type u64 + starttime: c.starttime, // Rule #2 for type u64 + endtime: c.endtime, // Rule #2 for type u64 + paywindow_start: c.paywindow_start, // Rule #2 for type u64 + paywindow_end: c.paywindow_end, // Rule #2 for type u64 + } + } +} + +#[allow(unused_variables)] +impl From for pb::FetchinvoiceResponse { + fn from(c: responses::FetchinvoiceResponse) -> Self { + Self { + invoice: c.invoice, // Rule #2 for type string + changes: Some(c.changes.into()), + next_period: c.next_period.map(|v| v.into()), + } + } +} + #[allow(unused_variables)] impl From for pb::FundchannelResponse { fn from(c: responses::FundchannelResponse) -> Self { @@ -1682,6 +1719,18 @@ impl From for pb::WaitblockheightResponse { } } +#[allow(unused_variables)] +impl From for pb::WaitResponse { + fn from(c: responses::WaitResponse) -> Self { + Self { + subsystem: c.subsystem as i32, + created: c.created, // Rule #2 for type u64? + updated: c.updated, // Rule #2 for type u64? + deleted: c.deleted, // Rule #2 for type u64? + } + } +} + #[allow(unused_variables)] impl From for pb::StopResponse { fn from(c: responses::StopResponse) -> Self { @@ -2270,6 +2319,22 @@ impl From for pb::FeeratesRequest { } } +#[allow(unused_variables)] +impl From for pb::FetchinvoiceRequest { + fn from(c: requests::FetchinvoiceRequest) -> Self { + Self { + offer: c.offer, // Rule #2 for type string + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + quantity: c.quantity, // Rule #2 for type u64? + recurrence_counter: c.recurrence_counter, // Rule #2 for type u64? + recurrence_start: c.recurrence_start, // Rule #2 for type number? + recurrence_label: c.recurrence_label, // Rule #2 for type string? + timeout: c.timeout, // Rule #2 for type number? + payer_note: c.payer_note, // Rule #2 for type string? + } + } +} + #[allow(unused_variables)] impl From for pb::FundchannelRequest { fn from(c: requests::FundchannelRequest) -> Self { @@ -2406,6 +2471,17 @@ impl From for pb::WaitblockheightRequest { } } +#[allow(unused_variables)] +impl From for pb::WaitRequest { + fn from(c: requests::WaitRequest) -> Self { + Self { + subsystem: c.subsystem as i32, + indexname: c.indexname as i32, + nextvalue: c.nextvalue, // Rule #2 for type u64 + } + } +} + #[allow(unused_variables)] impl From for pb::StopRequest { fn from(c: requests::StopRequest) -> Self { @@ -2983,6 +3059,22 @@ impl From for requests::FeeratesRequest { } } +#[allow(unused_variables)] +impl From for requests::FetchinvoiceRequest { + fn from(c: pb::FetchinvoiceRequest) -> Self { + Self { + offer: c.offer, // Rule #1 for type string + amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? + quantity: c.quantity, // Rule #1 for type u64? + recurrence_counter: c.recurrence_counter, // Rule #1 for type u64? + recurrence_start: c.recurrence_start, // Rule #1 for type number? + recurrence_label: c.recurrence_label, // Rule #1 for type string? + timeout: c.timeout, // Rule #1 for type number? + payer_note: c.payer_note, // Rule #1 for type string? + } + } +} + #[allow(unused_variables)] impl From for requests::FundchannelRequest { fn from(c: pb::FundchannelRequest) -> Self { @@ -3117,6 +3209,17 @@ impl From for requests::WaitblockheightRequest { } } +#[allow(unused_variables)] +impl From for requests::WaitRequest { + fn from(c: pb::WaitRequest) -> Self { + Self { + subsystem: c.subsystem.try_into().unwrap(), + indexname: c.indexname.try_into().unwrap(), + nextvalue: c.nextvalue, // Rule #1 for type u64 + } + } +} + #[allow(unused_variables)] impl From for requests::StopRequest { fn from(c: pb::StopRequest) -> Self { diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index c7d4e0cd0195..7519a344cc1c 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -1434,6 +1434,38 @@ async fn feerates( } +async fn fetch_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::FetchinvoiceRequest = req.into(); + debug!("Client asked for fetch_invoice"); + trace!("fetch_invoice request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::FetchInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method FetchInvoice: {:?}", e)))?; + match result { + Response::FetchInvoice(r) => { + trace!("fetch_invoice response: {:?}", r); + Ok(tonic::Response::new(r.into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call FetchInvoice", + r + ) + )), + } + +} + async fn fund_channel( &self, request: tonic::Request, @@ -1786,6 +1818,38 @@ async fn wait_block_height( } +async fn wait( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::WaitRequest = req.into(); + debug!("Client asked for wait"); + trace!("wait request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Wait(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Wait: {:?}", e)))?; + match result { + Response::Wait(r) => { + trace!("wait response: {:?}", r); + Ok(tonic::Response::new(r.into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Wait", + r + ) + )), + } + +} + async fn stop( &self, request: tonic::Request, diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index b81080a88a4f..5cb6576fc1b6 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -58,6 +58,7 @@ pub enum Request { Decode(requests::DecodeRequest), Disconnect(requests::DisconnectRequest), Feerates(requests::FeeratesRequest), + FetchInvoice(requests::FetchinvoiceRequest), FundChannel(requests::FundchannelRequest), GetRoute(requests::GetrouteRequest), ListForwards(requests::ListforwardsRequest), @@ -69,6 +70,7 @@ pub enum Request { SignInvoice(requests::SigninvoiceRequest), SignMessage(requests::SignmessageRequest), WaitBlockHeight(requests::WaitblockheightRequest), + Wait(requests::WaitRequest), Stop(requests::StopRequest), PreApproveKeysend(requests::PreapprovekeysendRequest), PreApproveInvoice(requests::PreapproveinvoiceRequest), @@ -123,6 +125,7 @@ pub enum Response { Decode(responses::DecodeResponse), Disconnect(responses::DisconnectResponse), Feerates(responses::FeeratesResponse), + FetchInvoice(responses::FetchinvoiceResponse), FundChannel(responses::FundchannelResponse), GetRoute(responses::GetrouteResponse), ListForwards(responses::ListforwardsResponse), @@ -134,6 +137,7 @@ pub enum Response { SignInvoice(responses::SigninvoiceResponse), SignMessage(responses::SignmessageResponse), WaitBlockHeight(responses::WaitblockheightResponse), + Wait(responses::WaitResponse), Stop(responses::StopResponse), PreApproveKeysend(responses::PreapprovekeysendResponse), PreApproveInvoice(responses::PreapproveinvoiceResponse), @@ -1280,6 +1284,35 @@ pub mod requests { type Response = super::responses::FeeratesResponse; } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FetchinvoiceRequest { + pub offer: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub quantity: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub recurrence_counter: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub recurrence_start: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub recurrence_label: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub payer_note: Option, + } + + impl From for Request { + fn from(r: FetchinvoiceRequest) -> Self { + Request::FetchInvoice(r) + } + } + + impl IntoRequest for FetchinvoiceRequest { + type Response = super::responses::FetchinvoiceResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundchannelRequest { pub id: PublicKey, @@ -1610,6 +1643,89 @@ pub mod requests { type Response = super::responses::WaitblockheightResponse; } + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] + pub enum WaitSubsystem { + #[serde(rename = "invoices")] + INVOICES, + #[serde(rename = "forwards")] + FORWARDS, + #[serde(rename = "sendpays")] + SENDPAYS, + } + + impl TryFrom for WaitSubsystem { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(WaitSubsystem::INVOICES), + 1 => Ok(WaitSubsystem::FORWARDS), + 2 => Ok(WaitSubsystem::SENDPAYS), + o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitSubsystem", o)), + } + } + } + + impl ToString for WaitSubsystem { + fn to_string(&self) -> String { + match self { + WaitSubsystem::INVOICES => "INVOICES", + WaitSubsystem::FORWARDS => "FORWARDS", + WaitSubsystem::SENDPAYS => "SENDPAYS", + }.to_string() + } + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] + pub enum WaitIndexname { + #[serde(rename = "created")] + CREATED, + #[serde(rename = "updated")] + UPDATED, + #[serde(rename = "deleted")] + DELETED, + } + + impl TryFrom for WaitIndexname { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(WaitIndexname::CREATED), + 1 => Ok(WaitIndexname::UPDATED), + 2 => Ok(WaitIndexname::DELETED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitIndexname", o)), + } + } + } + + impl ToString for WaitIndexname { + fn to_string(&self) -> String { + match self { + WaitIndexname::CREATED => "CREATED", + WaitIndexname::UPDATED => "UPDATED", + WaitIndexname::DELETED => "DELETED", + }.to_string() + } + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitRequest { + // Path `Wait.subsystem` + pub subsystem: WaitSubsystem, + // Path `Wait.indexname` + pub indexname: WaitIndexname, + pub nextvalue: u64, + } + + impl From for Request { + fn from(r: WaitRequest) -> Self { + Request::Wait(r) + } + } + + impl IntoRequest for WaitRequest { + type Response = super::responses::WaitResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct StopRequest { } @@ -4666,6 +4782,48 @@ pub mod responses { } } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FetchinvoiceChanges { + #[serde(skip_serializing_if = "Option::is_none")] + pub description_appended: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub vendor_removed: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub vendor: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FetchinvoiceNext_period { + pub counter: u64, + pub starttime: u64, + pub endtime: u64, + pub paywindow_start: u64, + pub paywindow_end: u64, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FetchinvoiceResponse { + pub invoice: String, + pub changes: FetchinvoiceChanges, + #[serde(skip_serializing_if = "Option::is_none")] + pub next_period: Option, + } + + impl TryFrom for FetchinvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::FetchInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundchannelResponse { pub tx: String, @@ -5101,6 +5259,61 @@ pub mod responses { } } + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] + pub enum WaitSubsystem { + #[serde(rename = "invoices")] + INVOICES, + #[serde(rename = "forwards")] + FORWARDS, + #[serde(rename = "sendpays")] + SENDPAYS, + } + + impl TryFrom for WaitSubsystem { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(WaitSubsystem::INVOICES), + 1 => Ok(WaitSubsystem::FORWARDS), + 2 => Ok(WaitSubsystem::SENDPAYS), + o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitSubsystem", o)), + } + } + } + + impl ToString for WaitSubsystem { + fn to_string(&self) -> String { + match self { + WaitSubsystem::INVOICES => "INVOICES", + WaitSubsystem::FORWARDS => "FORWARDS", + WaitSubsystem::SENDPAYS => "SENDPAYS", + }.to_string() + } + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitResponse { + // Path `Wait.subsystem` + pub subsystem: WaitSubsystem, + #[serde(skip_serializing_if = "Option::is_none")] + pub created: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub updated: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub deleted: Option, + } + + impl TryFrom for WaitResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Wait(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct StopResponse { } diff --git a/common/psbt_internal.c b/common/psbt_internal.c index aca36b6ad96a..df1cb536328d 100644 --- a/common/psbt_internal.c +++ b/common/psbt_internal.c @@ -124,6 +124,8 @@ psbt_to_witnesses(const tal_t *ctx, tal_arr(ctx, const struct witness *, 0); for (size_t i = 0; i < psbt->num_inputs; i++) { + struct wally_tx_witness_stack *wtx_s = + psbt->inputs[i].final_witness; if (!psbt_get_serial_id(&psbt->inputs[i].unknowns, &serial_id)) /* FIXME: throw an error ? */ @@ -136,9 +138,7 @@ psbt_to_witnesses(const tal_t *ctx, * - if is the *initiator*: * - MUST send even `serial_id`s */ - if (serial_id % 2 == side_to_stack) { - struct wally_tx_witness_stack *wtx_s = - psbt->inputs[i].final_witness; + if (wtx_s && serial_id % 2 == side_to_stack) { /* BOLT-e299850cb5ebd8bd9c55763bbc498fcdf94a9567 #2: * diff --git a/common/utils.h b/common/utils.h index 982492245d0f..3b37661a153d 100644 --- a/common/utils.h +++ b/common/utils.h @@ -136,7 +136,7 @@ extern const tal_t *wally_tal_ctx; /* Like mkstemp but resolves template relative to $TMPDIR (or /tmp if unset). * Returns created temporary path name at *created if successful. */ -int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created); +int tmpdir_mkstemp(const tal_t *ctx, const char *tmplt TAKES, char **created); /** * tal_strlowering - return the same string by in lower case. diff --git a/contrib/.gitignore b/contrib/.gitignore new file mode 100644 index 000000000000..f44fc41890ac --- /dev/null +++ b/contrib/.gitignore @@ -0,0 +1 @@ +/remote_hsmd diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py index ab52b1e715b6..a2679bf5e9e9 100644 --- a/contrib/msggen/msggen/utils/utils.py +++ b/contrib/msggen/msggen/utils/utils.py @@ -73,7 +73,7 @@ def load_jsonrpc_service(schema_dir: str): # "disableoffer", "Disconnect", "Feerates", - # "fetchinvoice", + "FetchInvoice", # "fundchannel_cancel", # "fundchannel_complete", "FundChannel", @@ -105,6 +105,7 @@ def load_jsonrpc_service(schema_dir: str): "SignMessage", # "unreserveinputs", "WaitBlockHeight", + "Wait", # "ListConfigs", # "check", # No point in mapping this one "Stop", diff --git a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py index 9714fc0b007b..2e54f125d12e 100644 --- a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py +++ b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py @@ -14,7 +14,7 @@ from pyln.grpc import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xc1\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x61lias\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x33\n\x0cour_features\x18\n \x01(\x0b\x32\x18.cln.GetinfoOur_featuresH\x01\x88\x01\x01\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x02\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_aliasB\x0f\n\r_our_featuresB\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xc4\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"G\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"\x8a\x02\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"_\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\r\n\tWEBSOCKET\x10\x05\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\x8e\x02\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x00\x88\x01\x01\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x42\x0f\n\r_num_channelsB\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x95\x18\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x38\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\n\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0c\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\r\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12\x38\n\x07\x66unding\x18\x13 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH \x88\x01\x01\x12\x34\n\x05\x61lias\x18\x32 \x01(\x0b\x32 .cln.ListpeersPeersChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\"\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H$\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH%\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H&\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH\'\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H(\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH)\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH*\x88\x01\x01\"\xe0\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\n\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0b\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\x0c\x42\x0f\n\r_scratch_txidB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xf3\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x00\x88\x01\x01\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\x42\x10\n\x0e_splice_amount\"\x9b\x02\n\x1dListpeersPeersChannelsFunding\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xf1\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\x83\x03\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\xab\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x01\x88\x01\x01\x42\r\n\x0b_channel_idB\x13\n\x11_short_channel_id\"\xdd\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0b \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xad\x05\n\x0fSendpayResponse\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x01\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x02\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x05\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x0b\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xb3\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x11\n\tdirection\x18\x10 \x01(\r\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\xb4\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xfe\x05\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12;\n\rpaid_outpoint\x18\x11 \x01(\x0b\x32\x1f.cln.CreateinvoicePaid_outpointH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\t\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\n\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x10\n\x0e_created_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"X\n\x1a\x43reateinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x17\n\x15\x44\x61tastoreusageRequest\"k\n\x16\x44\x61tastoreusageResponse\x12>\n\x0e\x64\x61tastoreusage\x18\x01 \x01(\x0b\x32!.cln.DatastoreusageDatastoreusageH\x00\x88\x01\x01\x42\x11\n\x0f_datastoreusage\"b\n\x1c\x44\x61tastoreusageDatastoreusage\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0btotal_bytes\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x06\n\x04_keyB\x0e\n\x0c_total_bytes\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xa1\x04\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x04\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x06\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x07\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xfa\x01\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\x95\x03\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1a\n\rcreated_index\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x03\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x05\x88\x01\x01\x42\x10\n\x0e_created_indexB\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xde\x02\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x12>\n\x05index\x18\x05 \x01(\x0e\x32*.cln.ListinvoicesRequest.ListinvoicesIndexH\x04\x88\x01\x01\x12\x12\n\x05start\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x12\n\x05limit\x18\x07 \x01(\rH\x06\x88\x01\x01\"-\n\x11ListinvoicesIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xd4\x06\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x07\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x08\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\t\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\n\x88\x01\x01\x12\x42\n\rpaid_outpoint\x18\x12 \x01(\x0b\x32&.cln.ListinvoicesInvoicesPaid_outpointH\x0b\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x0c\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"_\n!ListinvoicesInvoicesPaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\x8a\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12*\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x17.cln.SendonionFirst_hop\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\r \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xe7\x04\n\x11SendonionResponse\x12\x1a\n\rcreated_index\x18\x0e \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0f \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\t\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x10\n\x0e_updated_indexB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xa0\x03\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListsendpaysRequest.ListsendpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"-\n\x11ListsendpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd0\x05\n\x14ListsendpaysPayments\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x02\x88\x01\x01\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\n\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x10\n\x0e_created_indexB\t\n\x07_partidB\x10\n\x0e_updated_indexB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\xf8\x01\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputs\"S\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\"l\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\"\xda\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0e \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x10\n\x0e_localinvreqidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xe8\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"P\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\xbf\x05\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12<\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32 .cln.WaitanyinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"Y\n\x1bWaitanyinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xb0\x05\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12\x39\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1d.cln.WaitinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"V\n\x18WaitinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x8e\x05\n\x13WaitsendpayResponse\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\x04\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x05\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x10\n\x0e_created_indexB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_updated_indexB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x97\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\"3\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x08\n\x04P2TR\x10\x03\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"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\"\xda\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\"\x80\x03\n\x1dListpeerchannelsChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\n\x12\x1c\n\x18\x43HANNELD_AWAITING_SPLICE\x10\x0b\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0c\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\rB\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\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\"\xd0\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"N\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\xe8!\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12+\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x16.cln.DecodeOffer_paths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x37\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1c.cln.DecodeInvoice_fallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\x12\x16\n\tdecrypted\x18L \x01(\x0cH=\x88\x01\x01\"\x83\x01\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x12\x15\n\x11\x45MERGENCY_RECOVER\x10\x05\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hexB\x0c\n\n_decrypted\"<\n\x11\x44\x65\x63odeOffer_paths\x12\x15\n\rfirst_node_id\x18\x01 \x01(\x0c\x12\x10\n\x08\x62linding\x18\x02 \x01(\x0c\"\x8a\x01\n\x1f\x44\x65\x63odeOffer_recurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"T\n\x17\x44\x65\x63odeInvoice_pathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"Y\n\x17\x44\x65\x63odeInvoice_fallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"w\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x42,\n*_warning_invoice_fallbacks_version_invalid\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"\x9c\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x46\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32\".cln.FeeratesOnchain_fee_estimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\xd3\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\xd3\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\x9b\x02\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x30\n#unilateral_close_nonanchor_satoshis\x18\x06 \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\x42&\n$_unilateral_close_nonanchor_satoshis\"\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(\rH\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\xb7\x03\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListforwardsRequest.ListforwardsIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"-\n\x11ListforwardsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channelB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xba\x05\n\x14ListforwardsForwards\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x00\x88\x01\x01\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x01\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x04\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x05\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x10\n\x0e_created_indexB\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x10\n\x0e_updated_indexB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xff\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\t\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\n\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"*\n\x10ListhtlcsRequest\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListhtlcsResponse\x12\"\n\x05htlcs\x18\x01 \x03(\x0b\x32\x13.cln.ListhtlcsHtlcs\"\x89\x02\n\x0eListhtlcsHtlcs\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12>\n\tdirection\x18\x05 \x01(\x0e\x32+.cln.ListhtlcsHtlcs.ListhtlcsHtlcsDirection\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1d\n\x05state\x18\x07 \x01(\x0e\x32\x0e.cln.HtlcState\"*\n\x17ListhtlcsHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xaa\x02\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x19\n\x0c\x65nforcedelay\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1c\n\x0fignorefeelimits\x18\x07 \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelayB\x12\n\x10_ignorefeelimits\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\xca\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12\x1e\n\x11ignore_fee_limits\x18\n \x01(\x08H\x01\x88\x01\x01\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x02\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x03\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x14\n\x12_ignore_fee_limitsB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"O\n\x16WaitblockheightRequest\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\x12\x14\n\x07timeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_timeout\".\n\x17WaitblockheightResponse\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse\"\xa7\x01\n\x18PreapprovekeysendRequest\x12\x18\n\x0b\x64\x65stination\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_destinationB\x0f\n\r_payment_hashB\x0e\n\x0c_amount_msat\"\x1b\n\x19PreapprovekeysendResponse\":\n\x18PreapproveinvoiceRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_bolt11\"\x1b\n\x19PreapproveinvoiceResponse\"\x15\n\x13StaticbackupRequest\"#\n\x14StaticbackupResponse\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\x32\xac\x1e\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12K\n\x0e\x44\x61tastoreUsage\x12\x1a.cln.DatastoreusageRequest\x1a\x1b.cln.DatastoreusageResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12<\n\tListHtlcs\x12\x15.cln.ListhtlcsRequest\x1a\x16.cln.ListhtlcsResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12N\n\x0fWaitBlockHeight\x12\x1b.cln.WaitblockheightRequest\x1a\x1c.cln.WaitblockheightResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x12T\n\x11PreApproveKeysend\x12\x1d.cln.PreapprovekeysendRequest\x1a\x1e.cln.PreapprovekeysendResponse\"\x00\x12T\n\x11PreApproveInvoice\x12\x1d.cln.PreapproveinvoiceRequest\x1a\x1e.cln.PreapproveinvoiceResponse\"\x00\x12\x45\n\x0cStaticBackup\x12\x18.cln.StaticbackupRequest\x1a\x19.cln.StaticbackupResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xc1\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x61lias\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x33\n\x0cour_features\x18\n \x01(\x0b\x32\x18.cln.GetinfoOur_featuresH\x01\x88\x01\x01\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x02\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_aliasB\x0f\n\r_our_featuresB\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xc4\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"G\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"\x8a\x02\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"_\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\r\n\tWEBSOCKET\x10\x05\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\x8e\x02\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x00\x88\x01\x01\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x42\x0f\n\r_num_channelsB\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x95\x18\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x38\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\n\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0c\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\r\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12\x38\n\x07\x66unding\x18\x13 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH \x88\x01\x01\x12\x34\n\x05\x61lias\x18\x32 \x01(\x0b\x32 .cln.ListpeersPeersChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\"\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H$\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH%\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H&\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH\'\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H(\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH)\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH*\x88\x01\x01\"\xe0\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\n\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0b\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\x0c\x42\x0f\n\r_scratch_txidB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xf3\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x00\x88\x01\x01\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\x42\x10\n\x0e_splice_amount\"\x9b\x02\n\x1dListpeersPeersChannelsFunding\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xf1\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\x83\x03\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\xab\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x01\x88\x01\x01\x42\r\n\x0b_channel_idB\x13\n\x11_short_channel_id\"\xdd\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0b \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xad\x05\n\x0fSendpayResponse\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x01\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x02\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x05\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x0b\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xb3\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x11\n\tdirection\x18\x10 \x01(\r\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\xb4\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xfe\x05\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12;\n\rpaid_outpoint\x18\x11 \x01(\x0b\x32\x1f.cln.CreateinvoicePaid_outpointH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\t\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\n\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x10\n\x0e_created_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"X\n\x1a\x43reateinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x17\n\x15\x44\x61tastoreusageRequest\"k\n\x16\x44\x61tastoreusageResponse\x12>\n\x0e\x64\x61tastoreusage\x18\x01 \x01(\x0b\x32!.cln.DatastoreusageDatastoreusageH\x00\x88\x01\x01\x42\x11\n\x0f_datastoreusage\"b\n\x1c\x44\x61tastoreusageDatastoreusage\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0btotal_bytes\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x06\n\x04_keyB\x0e\n\x0c_total_bytes\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xa1\x04\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x04\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x06\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x07\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xfa\x01\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\x95\x03\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1a\n\rcreated_index\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x03\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x05\x88\x01\x01\x42\x10\n\x0e_created_indexB\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xde\x02\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x12>\n\x05index\x18\x05 \x01(\x0e\x32*.cln.ListinvoicesRequest.ListinvoicesIndexH\x04\x88\x01\x01\x12\x12\n\x05start\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x12\n\x05limit\x18\x07 \x01(\rH\x06\x88\x01\x01\"-\n\x11ListinvoicesIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xd4\x06\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x07\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x08\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\t\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\n\x88\x01\x01\x12\x42\n\rpaid_outpoint\x18\x12 \x01(\x0b\x32&.cln.ListinvoicesInvoicesPaid_outpointH\x0b\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x0c\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"_\n!ListinvoicesInvoicesPaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\x8a\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12*\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x17.cln.SendonionFirst_hop\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\r \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xe7\x04\n\x11SendonionResponse\x12\x1a\n\rcreated_index\x18\x0e \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0f \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\t\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x10\n\x0e_created_indexB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x10\n\x0e_updated_indexB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xa0\x03\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListsendpaysRequest.ListsendpaysIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"-\n\x11ListsendpaysIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_statusB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd0\x05\n\x14ListsendpaysPayments\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x02\x88\x01\x01\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x06\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x07\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\n\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x10\n\x0e_created_indexB\t\n\x07_partidB\x10\n\x0e_updated_indexB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\xf8\x01\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputs\"S\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\"l\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\"\xda\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0e \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x10\n\x0e_localinvreqidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xe8\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"P\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\xbf\x05\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12<\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32 .cln.WaitanyinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"Y\n\x1bWaitanyinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xb0\x05\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12\x39\n\rpaid_outpoint\x18\x0f \x01(\x0b\x32\x1d.cln.WaitinvoicePaid_outpointH\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\t\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x10\n\x0e_paid_outpointB\x13\n\x11_payment_preimage\"V\n\x18WaitinvoicePaid_outpoint\x12\x11\n\x04txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x13\n\x06outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_txidB\t\n\x07_outnum\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x8e\x05\n\x13WaitsendpayResponse\x12\x1a\n\rcreated_index\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x1a\n\rupdated_index\x18\x10 \x01(\x04H\x04\x88\x01\x01\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x05\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x07\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\n\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x10\n\x0e_created_indexB\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_updated_indexB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x97\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\"3\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x08\n\x04P2TR\x10\x03\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"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\"\xda\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\"\x80\x03\n\x1dListpeerchannelsChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\n\x12\x1c\n\x18\x43HANNELD_AWAITING_SPLICE\x10\x0b\x12\x1c\n\x18\x44UALOPEND_OPEN_COMMITTED\x10\x0c\x12\x1f\n\x1b\x44UALOPEND_OPEN_COMMIT_READY\x10\rB\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\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\"\xd0\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"N\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x12\x08\n\x04P2TR\x10\x04\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\xe8!\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12+\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x16.cln.DecodeOffer_paths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x37\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1c.cln.DecodeInvoice_fallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\x12\x16\n\tdecrypted\x18L \x01(\x0cH=\x88\x01\x01\"\x83\x01\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x12\x15\n\x11\x45MERGENCY_RECOVER\x10\x05\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hexB\x0c\n\n_decrypted\"<\n\x11\x44\x65\x63odeOffer_paths\x12\x15\n\rfirst_node_id\x18\x01 \x01(\x0c\x12\x10\n\x08\x62linding\x18\x02 \x01(\x0c\"\x8a\x01\n\x1f\x44\x65\x63odeOffer_recurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"T\n\x17\x44\x65\x63odeInvoice_pathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"Y\n\x17\x44\x65\x63odeInvoice_fallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"w\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x42,\n*_warning_invoice_fallbacks_version_invalid\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"\x9c\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x46\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32\".cln.FeeratesOnchain_fee_estimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\xd3\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\xd3\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\x9b\x02\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x30\n#unilateral_close_nonanchor_satoshis\x18\x06 \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\x42&\n$_unilateral_close_nonanchor_satoshis\"\xe9\x02\n\x13\x46\x65tchinvoiceRequest\x12\r\n\x05offer\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x15\n\x08quantity\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x1f\n\x12recurrence_counter\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x10recurrence_start\x18\x05 \x01(\x01H\x03\x88\x01\x01\x12\x1d\n\x10recurrence_label\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07timeout\x18\x07 \x01(\x01H\x05\x88\x01\x01\x12\x17\n\npayer_note\x18\x08 \x01(\tH\x06\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x0b\n\t_quantityB\x15\n\x13_recurrence_counterB\x13\n\x11_recurrence_startB\x13\n\x11_recurrence_labelB\n\n\x08_timeoutB\r\n\x0b_payer_note\"\x9a\x01\n\x14\x46\x65tchinvoiceResponse\x12\x0f\n\x07invoice\x18\x01 \x01(\t\x12)\n\x07\x63hanges\x18\x02 \x01(\x0b\x32\x18.cln.FetchinvoiceChanges\x12\x36\n\x0bnext_period\x18\x03 \x01(\x0b\x32\x1c.cln.FetchinvoiceNext_periodH\x00\x88\x01\x01\x42\x0e\n\x0c_next_period\"\x82\x02\n\x13\x46\x65tchinvoiceChanges\x12!\n\x14\x64\x65scription_appended\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0evendor_removed\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06vendor\x18\x04 \x01(\tH\x03\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x17\n\x15_description_appendedB\x0e\n\x0c_descriptionB\x11\n\x0f_vendor_removedB\t\n\x07_vendorB\x0e\n\x0c_amount_msat\"~\n\x17\x46\x65tchinvoiceNext_period\x12\x0f\n\x07\x63ounter\x18\x01 \x01(\x04\x12\x11\n\tstarttime\x18\x02 \x01(\x04\x12\x0f\n\x07\x65ndtime\x18\x03 \x01(\x04\x12\x17\n\x0fpaywindow_start\x18\x04 \x01(\x04\x12\x15\n\rpaywindow_end\x18\x05 \x01(\x04\"\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(\rH\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\xb7\x03\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\x12>\n\x05index\x18\x04 \x01(\x0e\x32*.cln.ListforwardsRequest.ListforwardsIndexH\x03\x88\x01\x01\x12\x12\n\x05start\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05limit\x18\x06 \x01(\rH\x05\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"-\n\x11ListforwardsIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channelB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xba\x05\n\x14ListforwardsForwards\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x00\x88\x01\x01\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x01\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x04\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x05\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x10\n\x0e_created_indexB\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x10\n\x0e_updated_indexB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xff\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\t\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\n\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"*\n\x10ListhtlcsRequest\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListhtlcsResponse\x12\"\n\x05htlcs\x18\x01 \x03(\x0b\x32\x13.cln.ListhtlcsHtlcs\"\x89\x02\n\x0eListhtlcsHtlcs\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12>\n\tdirection\x18\x05 \x01(\x0e\x32+.cln.ListhtlcsHtlcs.ListhtlcsHtlcsDirection\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1d\n\x05state\x18\x07 \x01(\x0e\x32\x0e.cln.HtlcState\"*\n\x17ListhtlcsHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xaa\x02\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x19\n\x0c\x65nforcedelay\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1c\n\x0fignorefeelimits\x18\x07 \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelayB\x12\n\x10_ignorefeelimits\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\xca\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12\x1e\n\x11ignore_fee_limits\x18\n \x01(\x08H\x01\x88\x01\x01\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x02\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x03\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x14\n\x12_ignore_fee_limitsB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"O\n\x16WaitblockheightRequest\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\x12\x14\n\x07timeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x42\n\n\x08_timeout\".\n\x17WaitblockheightResponse\x12\x13\n\x0b\x62lockheight\x18\x01 \x01(\r\"\xf9\x01\n\x0bWaitRequest\x12\x31\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitSubsystem\x12\x31\n\tindexname\x18\x02 \x01(\x0e\x32\x1e.cln.WaitRequest.WaitIndexname\x12\x11\n\tnextvalue\x18\x03 \x01(\x04\"9\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\"6\n\rWaitIndexname\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x12\x0b\n\x07\x44\x45LETED\x10\x02\"\xe3\x01\n\x0cWaitResponse\x12\x32\n\tsubsystem\x18\x01 \x01(\x0e\x32\x1f.cln.WaitResponse.WaitSubsystem\x12\x14\n\x07\x63reated\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07updated\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07\x64\x65leted\x18\x04 \x01(\x04H\x02\x88\x01\x01\"9\n\rWaitSubsystem\x12\x0c\n\x08INVOICES\x10\x00\x12\x0c\n\x08\x46ORWARDS\x10\x01\x12\x0c\n\x08SENDPAYS\x10\x02\x42\n\n\x08_createdB\n\n\x08_updatedB\n\n\x08_deleted\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse\"\xa7\x01\n\x18PreapprovekeysendRequest\x12\x18\n\x0b\x64\x65stination\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_destinationB\x0f\n\r_payment_hashB\x0e\n\x0c_amount_msat\"\x1b\n\x19PreapprovekeysendResponse\":\n\x18PreapproveinvoiceRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_bolt11\"\x1b\n\x19PreapproveinvoiceResponse\"\x15\n\x13StaticbackupRequest\"#\n\x14StaticbackupResponse\x12\x0b\n\x03scb\x18\x01 \x03(\x0c\x32\xa2\x1f\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12K\n\x0e\x44\x61tastoreUsage\x12\x1a.cln.DatastoreusageRequest\x1a\x1b.cln.DatastoreusageResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x45\n\x0c\x46\x65tchInvoice\x12\x18.cln.FetchinvoiceRequest\x1a\x19.cln.FetchinvoiceResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12<\n\tListHtlcs\x12\x15.cln.ListhtlcsRequest\x1a\x16.cln.ListhtlcsResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12N\n\x0fWaitBlockHeight\x12\x1b.cln.WaitblockheightRequest\x1a\x1c.cln.WaitblockheightResponse\"\x00\x12-\n\x04Wait\x12\x10.cln.WaitRequest\x1a\x11.cln.WaitResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x12T\n\x11PreApproveKeysend\x12\x1d.cln.PreapprovekeysendRequest\x1a\x1e.cln.PreapprovekeysendResponse\"\x00\x12T\n\x11PreApproveInvoice\x12\x1d.cln.PreapproveinvoiceRequest\x1a\x1e.cln.PreapproveinvoiceResponse\"\x00\x12\x45\n\x0cStaticBackup\x12\x18.cln.StaticbackupRequest\x1a\x19.cln.StaticbackupResponse\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -373,92 +373,110 @@ _globals['_FEERATESPERKWESTIMATES']._serialized_end=39546 _globals['_FEERATESONCHAIN_FEE_ESTIMATES']._serialized_start=39549 _globals['_FEERATESONCHAIN_FEE_ESTIMATES']._serialized_end=39832 - _globals['_FUNDCHANNELREQUEST']._serialized_start=39835 - _globals['_FUNDCHANNELREQUEST']._serialized_end=40320 - _globals['_FUNDCHANNELRESPONSE']._serialized_start=40323 - _globals['_FUNDCHANNELRESPONSE']._serialized_end=40478 - _globals['_GETROUTEREQUEST']._serialized_start=40481 - _globals['_GETROUTEREQUEST']._serialized_end=40717 - _globals['_GETROUTERESPONSE']._serialized_start=40719 - _globals['_GETROUTERESPONSE']._serialized_end=40772 - _globals['_GETROUTEROUTE']._serialized_start=40775 - _globals['_GETROUTEROUTE']._serialized_end=40972 - _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_start=40943 - _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_end=40972 - _globals['_LISTFORWARDSREQUEST']._serialized_start=40975 - _globals['_LISTFORWARDSREQUEST']._serialized_end=41414 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_start=41219 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_end=41295 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_start=41297 - _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_end=41342 - _globals['_LISTFORWARDSRESPONSE']._serialized_start=41416 - _globals['_LISTFORWARDSRESPONSE']._serialized_end=41483 - _globals['_LISTFORWARDSFORWARDS']._serialized_start=41486 - _globals['_LISTFORWARDSFORWARDS']._serialized_end=42184 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_start=41931 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_end=42015 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_start=42017 - _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_end=42065 - _globals['_LISTPAYSREQUEST']._serialized_start=42187 - _globals['_LISTPAYSREQUEST']._serialized_end=42406 - _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_start=42312 - _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_end=42367 - _globals['_LISTPAYSRESPONSE']._serialized_start=42408 - _globals['_LISTPAYSRESPONSE']._serialized_end=42459 - _globals['_LISTPAYSPAYS']._serialized_start=42462 - _globals['_LISTPAYSPAYS']._serialized_end=43101 - _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_start=42876 - _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_end=42935 - _globals['_LISTHTLCSREQUEST']._serialized_start=43103 - _globals['_LISTHTLCSREQUEST']._serialized_end=43145 - _globals['_LISTHTLCSRESPONSE']._serialized_start=43147 - _globals['_LISTHTLCSRESPONSE']._serialized_end=43202 - _globals['_LISTHTLCSHTLCS']._serialized_start=43205 - _globals['_LISTHTLCSHTLCS']._serialized_end=43470 - _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_start=43428 - _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_end=43470 - _globals['_PINGREQUEST']._serialized_start=43472 - _globals['_PINGREQUEST']._serialized_end=43561 - _globals['_PINGRESPONSE']._serialized_start=43563 - _globals['_PINGRESPONSE']._serialized_end=43593 - _globals['_SENDCUSTOMMSGREQUEST']._serialized_start=43595 - _globals['_SENDCUSTOMMSGREQUEST']._serialized_end=43647 - _globals['_SENDCUSTOMMSGRESPONSE']._serialized_start=43649 - _globals['_SENDCUSTOMMSGRESPONSE']._serialized_end=43688 - _globals['_SETCHANNELREQUEST']._serialized_start=43691 - _globals['_SETCHANNELREQUEST']._serialized_end=43989 - _globals['_SETCHANNELRESPONSE']._serialized_start=43991 - _globals['_SETCHANNELRESPONSE']._serialized_end=44054 - _globals['_SETCHANNELCHANNELS']._serialized_start=44057 - _globals['_SETCHANNELCHANNELS']._serialized_end=44515 - _globals['_SIGNINVOICEREQUEST']._serialized_start=44517 - _globals['_SIGNINVOICEREQUEST']._serialized_end=44556 - _globals['_SIGNINVOICERESPONSE']._serialized_start=44558 - _globals['_SIGNINVOICERESPONSE']._serialized_end=44595 - _globals['_SIGNMESSAGEREQUEST']._serialized_start=44597 - _globals['_SIGNMESSAGEREQUEST']._serialized_end=44634 - _globals['_SIGNMESSAGERESPONSE']._serialized_start=44636 - _globals['_SIGNMESSAGERESPONSE']._serialized_end=44706 - _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_start=44708 - _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_end=44787 - _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_start=44789 - _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_end=44835 - _globals['_STOPREQUEST']._serialized_start=44837 - _globals['_STOPREQUEST']._serialized_end=44850 - _globals['_STOPRESPONSE']._serialized_start=44852 - _globals['_STOPRESPONSE']._serialized_end=44866 - _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_start=44869 - _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_end=45036 - _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_start=45038 - _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_end=45065 - _globals['_PREAPPROVEINVOICEREQUEST']._serialized_start=45067 - _globals['_PREAPPROVEINVOICEREQUEST']._serialized_end=45125 - _globals['_PREAPPROVEINVOICERESPONSE']._serialized_start=45127 - _globals['_PREAPPROVEINVOICERESPONSE']._serialized_end=45154 - _globals['_STATICBACKUPREQUEST']._serialized_start=45156 - _globals['_STATICBACKUPREQUEST']._serialized_end=45177 - _globals['_STATICBACKUPRESPONSE']._serialized_start=45179 - _globals['_STATICBACKUPRESPONSE']._serialized_end=45214 - _globals['_NODE']._serialized_start=45217 - _globals['_NODE']._serialized_end=49101 + _globals['_FETCHINVOICEREQUEST']._serialized_start=39835 + _globals['_FETCHINVOICEREQUEST']._serialized_end=40196 + _globals['_FETCHINVOICERESPONSE']._serialized_start=40199 + _globals['_FETCHINVOICERESPONSE']._serialized_end=40353 + _globals['_FETCHINVOICECHANGES']._serialized_start=40356 + _globals['_FETCHINVOICECHANGES']._serialized_end=40614 + _globals['_FETCHINVOICENEXT_PERIOD']._serialized_start=40616 + _globals['_FETCHINVOICENEXT_PERIOD']._serialized_end=40742 + _globals['_FUNDCHANNELREQUEST']._serialized_start=40745 + _globals['_FUNDCHANNELREQUEST']._serialized_end=41230 + _globals['_FUNDCHANNELRESPONSE']._serialized_start=41233 + _globals['_FUNDCHANNELRESPONSE']._serialized_end=41388 + _globals['_GETROUTEREQUEST']._serialized_start=41391 + _globals['_GETROUTEREQUEST']._serialized_end=41627 + _globals['_GETROUTERESPONSE']._serialized_start=41629 + _globals['_GETROUTERESPONSE']._serialized_end=41682 + _globals['_GETROUTEROUTE']._serialized_start=41685 + _globals['_GETROUTEROUTE']._serialized_end=41882 + _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_start=41853 + _globals['_GETROUTEROUTE_GETROUTEROUTESTYLE']._serialized_end=41882 + _globals['_LISTFORWARDSREQUEST']._serialized_start=41885 + _globals['_LISTFORWARDSREQUEST']._serialized_end=42324 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_start=42129 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS']._serialized_end=42205 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_start=42207 + _globals['_LISTFORWARDSREQUEST_LISTFORWARDSINDEX']._serialized_end=42252 + _globals['_LISTFORWARDSRESPONSE']._serialized_start=42326 + _globals['_LISTFORWARDSRESPONSE']._serialized_end=42393 + _globals['_LISTFORWARDSFORWARDS']._serialized_start=42396 + _globals['_LISTFORWARDSFORWARDS']._serialized_end=43094 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_start=42841 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS']._serialized_end=42925 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_start=42927 + _globals['_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE']._serialized_end=42975 + _globals['_LISTPAYSREQUEST']._serialized_start=43097 + _globals['_LISTPAYSREQUEST']._serialized_end=43316 + _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_start=43222 + _globals['_LISTPAYSREQUEST_LISTPAYSSTATUS']._serialized_end=43277 + _globals['_LISTPAYSRESPONSE']._serialized_start=43318 + _globals['_LISTPAYSRESPONSE']._serialized_end=43369 + _globals['_LISTPAYSPAYS']._serialized_start=43372 + _globals['_LISTPAYSPAYS']._serialized_end=44011 + _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_start=43786 + _globals['_LISTPAYSPAYS_LISTPAYSPAYSSTATUS']._serialized_end=43845 + _globals['_LISTHTLCSREQUEST']._serialized_start=44013 + _globals['_LISTHTLCSREQUEST']._serialized_end=44055 + _globals['_LISTHTLCSRESPONSE']._serialized_start=44057 + _globals['_LISTHTLCSRESPONSE']._serialized_end=44112 + _globals['_LISTHTLCSHTLCS']._serialized_start=44115 + _globals['_LISTHTLCSHTLCS']._serialized_end=44380 + _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_start=44338 + _globals['_LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION']._serialized_end=44380 + _globals['_PINGREQUEST']._serialized_start=44382 + _globals['_PINGREQUEST']._serialized_end=44471 + _globals['_PINGRESPONSE']._serialized_start=44473 + _globals['_PINGRESPONSE']._serialized_end=44503 + _globals['_SENDCUSTOMMSGREQUEST']._serialized_start=44505 + _globals['_SENDCUSTOMMSGREQUEST']._serialized_end=44557 + _globals['_SENDCUSTOMMSGRESPONSE']._serialized_start=44559 + _globals['_SENDCUSTOMMSGRESPONSE']._serialized_end=44598 + _globals['_SETCHANNELREQUEST']._serialized_start=44601 + _globals['_SETCHANNELREQUEST']._serialized_end=44899 + _globals['_SETCHANNELRESPONSE']._serialized_start=44901 + _globals['_SETCHANNELRESPONSE']._serialized_end=44964 + _globals['_SETCHANNELCHANNELS']._serialized_start=44967 + _globals['_SETCHANNELCHANNELS']._serialized_end=45425 + _globals['_SIGNINVOICEREQUEST']._serialized_start=45427 + _globals['_SIGNINVOICEREQUEST']._serialized_end=45466 + _globals['_SIGNINVOICERESPONSE']._serialized_start=45468 + _globals['_SIGNINVOICERESPONSE']._serialized_end=45505 + _globals['_SIGNMESSAGEREQUEST']._serialized_start=45507 + _globals['_SIGNMESSAGEREQUEST']._serialized_end=45544 + _globals['_SIGNMESSAGERESPONSE']._serialized_start=45546 + _globals['_SIGNMESSAGERESPONSE']._serialized_end=45616 + _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_start=45618 + _globals['_WAITBLOCKHEIGHTREQUEST']._serialized_end=45697 + _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_start=45699 + _globals['_WAITBLOCKHEIGHTRESPONSE']._serialized_end=45745 + _globals['_WAITREQUEST']._serialized_start=45748 + _globals['_WAITREQUEST']._serialized_end=45997 + _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_start=45884 + _globals['_WAITREQUEST_WAITSUBSYSTEM']._serialized_end=45941 + _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_start=45943 + _globals['_WAITREQUEST_WAITINDEXNAME']._serialized_end=45997 + _globals['_WAITRESPONSE']._serialized_start=46000 + _globals['_WAITRESPONSE']._serialized_end=46227 + _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_start=45884 + _globals['_WAITRESPONSE_WAITSUBSYSTEM']._serialized_end=45941 + _globals['_STOPREQUEST']._serialized_start=46229 + _globals['_STOPREQUEST']._serialized_end=46242 + _globals['_STOPRESPONSE']._serialized_start=46244 + _globals['_STOPRESPONSE']._serialized_end=46258 + _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_start=46261 + _globals['_PREAPPROVEKEYSENDREQUEST']._serialized_end=46428 + _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_start=46430 + _globals['_PREAPPROVEKEYSENDRESPONSE']._serialized_end=46457 + _globals['_PREAPPROVEINVOICEREQUEST']._serialized_start=46459 + _globals['_PREAPPROVEINVOICEREQUEST']._serialized_end=46517 + _globals['_PREAPPROVEINVOICERESPONSE']._serialized_start=46519 + _globals['_PREAPPROVEINVOICERESPONSE']._serialized_end=46546 + _globals['_STATICBACKUPREQUEST']._serialized_start=46548 + _globals['_STATICBACKUPREQUEST']._serialized_end=46569 + _globals['_STATICBACKUPRESPONSE']._serialized_start=46571 + _globals['_STATICBACKUPRESPONSE']._serialized_end=46606 + _globals['_NODE']._serialized_start=46609 + _globals['_NODE']._serialized_end=50611 # @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py index 2ac523c4b7b5..6f0c2bf8d294 100644 --- a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py +++ b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py @@ -234,6 +234,11 @@ def __init__(self, channel): request_serializer=node__pb2.FeeratesRequest.SerializeToString, response_deserializer=node__pb2.FeeratesResponse.FromString, ) + self.FetchInvoice = channel.unary_unary( + '/cln.Node/FetchInvoice', + request_serializer=node__pb2.FetchinvoiceRequest.SerializeToString, + response_deserializer=node__pb2.FetchinvoiceResponse.FromString, + ) self.FundChannel = channel.unary_unary( '/cln.Node/FundChannel', request_serializer=node__pb2.FundchannelRequest.SerializeToString, @@ -289,6 +294,11 @@ def __init__(self, channel): request_serializer=node__pb2.WaitblockheightRequest.SerializeToString, response_deserializer=node__pb2.WaitblockheightResponse.FromString, ) + self.Wait = channel.unary_unary( + '/cln.Node/Wait', + request_serializer=node__pb2.WaitRequest.SerializeToString, + response_deserializer=node__pb2.WaitResponse.FromString, + ) self.Stop = channel.unary_unary( '/cln.Node/Stop', request_serializer=node__pb2.StopRequest.SerializeToString, @@ -578,6 +588,12 @@ def Feerates(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def FetchInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def FundChannel(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -644,6 +660,12 @@ def WaitBlockHeight(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def Wait(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def Stop(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -891,6 +913,11 @@ def add_NodeServicer_to_server(servicer, server): request_deserializer=node__pb2.FeeratesRequest.FromString, response_serializer=node__pb2.FeeratesResponse.SerializeToString, ), + 'FetchInvoice': grpc.unary_unary_rpc_method_handler( + servicer.FetchInvoice, + request_deserializer=node__pb2.FetchinvoiceRequest.FromString, + response_serializer=node__pb2.FetchinvoiceResponse.SerializeToString, + ), 'FundChannel': grpc.unary_unary_rpc_method_handler( servicer.FundChannel, request_deserializer=node__pb2.FundchannelRequest.FromString, @@ -946,6 +973,11 @@ def add_NodeServicer_to_server(servicer, server): request_deserializer=node__pb2.WaitblockheightRequest.FromString, response_serializer=node__pb2.WaitblockheightResponse.SerializeToString, ), + 'Wait': grpc.unary_unary_rpc_method_handler( + servicer.Wait, + request_deserializer=node__pb2.WaitRequest.FromString, + response_serializer=node__pb2.WaitResponse.SerializeToString, + ), 'Stop': grpc.unary_unary_rpc_method_handler( servicer.Stop, request_deserializer=node__pb2.StopRequest.FromString, @@ -1724,6 +1756,23 @@ def Feerates(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def FetchInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/FetchInvoice', + node__pb2.FetchinvoiceRequest.SerializeToString, + node__pb2.FetchinvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def FundChannel(request, target, @@ -1911,6 +1960,23 @@ def WaitBlockHeight(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def Wait(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Wait', + node__pb2.WaitRequest.SerializeToString, + node__pb2.WaitResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def Stop(request, target, diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index 4bfce3578d1d..17a7ec27a69e 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -1,6 +1,6 @@ from concurrent import futures from pyln.testing.db import SqliteDbProvider, PostgresDbProvider -from pyln.testing.utils import NodeFactory, BitcoinD, ElementsD, env, LightningNode, TEST_DEBUG +from pyln.testing.utils import NodeFactory, BitcoinD, ElementsD, env, LightningNode, TEST_DEBUG, LssD from pyln.client import Millisatoshi from typing import Dict @@ -31,6 +31,9 @@ def test_base_dir(): yield directory + if bool(int(os.getenv('TEST_KEEPDIR', '0'))): + return + # Now check if any test directory is left because the corresponding test # failed. If there are no such tests we can clean up the root test # directory. @@ -92,7 +95,7 @@ def directory(request, test_base_dir, test_name): outcome = 'passed' if rep_call is None else rep_call.outcome failed = not outcome or request.node.has_errors or outcome != 'passed' - if not failed: + if not failed and not bool(int(os.getenv('TEST_KEEPDIR', '0'))): try: shutil.rmtree(directory) except OSError: @@ -164,6 +167,25 @@ def bitcoind(directory, teardown_checks): bitcoind.proc.wait() +@pytest.fixture +def lssd(directory, teardown_checks): + lssd = LssD(directory) + + try: + lssd.start() + except Exception: + lssd.stop() + raise + + yield lssd + + try: + lssd.stop() + except Exception: + lssd.proc.kill() + lssd.proc.wait() + + class TeardownErrors(object): def __init__(self): self.errors = [] @@ -446,11 +468,12 @@ def jsonschemas(): @pytest.fixture -def node_factory(request, directory, test_name, bitcoind, executor, db_provider, teardown_checks, node_cls, jsonschemas): +def node_factory(request, directory, test_name, bitcoind, lssd, executor, db_provider, teardown_checks, node_cls, jsonschemas): nf = NodeFactory( request, test_name, bitcoind, + lssd, executor, directory=directory, db_provider=db_provider, diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index c519c35101e6..8c00b2a27087 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -1268,6 +1268,32 @@ def feerates2py(m): }) +def fetchinvoice_changes2py(m): + return remove_default({ + "description_appended": m.description_appended, # PrimitiveField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "vendor_removed": m.vendor_removed, # PrimitiveField in generate_composite + "vendor": m.vendor, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + }) + + +def fetchinvoice_next_period2py(m): + return remove_default({ + "counter": m.counter, # PrimitiveField in generate_composite + "starttime": m.starttime, # PrimitiveField in generate_composite + "endtime": m.endtime, # PrimitiveField in generate_composite + "paywindow_start": m.paywindow_start, # PrimitiveField in generate_composite + "paywindow_end": m.paywindow_end, # PrimitiveField in generate_composite + }) + + +def fetchinvoice2py(m): + return remove_default({ + "invoice": m.invoice, # PrimitiveField in generate_composite + }) + + def fundchannel2py(m): return remove_default({ "tx": hexlify(m.tx), # PrimitiveField in generate_composite @@ -1415,6 +1441,15 @@ def waitblockheight2py(m): }) +def wait2py(m): + return remove_default({ + "subsystem": str(m.subsystem), # EnumField in generate_composite + "created": m.created, # PrimitiveField in generate_composite + "updated": m.updated, # PrimitiveField in generate_composite + "deleted": m.deleted, # PrimitiveField in generate_composite + }) + + def stop2py(m): return remove_default({ }) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 7f39637d2c76..c876c23e378c 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -78,6 +78,7 @@ def env(name, default=None): SLOW_MACHINE = env("SLOW_MACHINE", "0") == "1" DEPRECATED_APIS = env("DEPRECATED_APIS", "0") == "1" TIMEOUT = int(env("TIMEOUT", 180 if SLOW_MACHINE else 60)) +SUBDAEMON = env("SUBDAEMON", "") EXPERIMENTAL_DUAL_FUND = env("EXPERIMENTAL_DUAL_FUND", "0") == "1" EXPERIMENTAL_SPLICING = env("EXPERIMENTAL_SPLICING", "0") == "1" @@ -383,6 +384,45 @@ def f(*args): return f +class LssD(TailableProc): + def __init__(self, directory, rpcport=None): + lss_dir = os.path.join(directory, 'lss') + TailableProc.__init__(self, lss_dir, verbose=False) + + if rpcport is None: + self.reserved_rpcport = reserve_unused_port() + rpcport = self.reserved_rpcport + else: + self.reserved_rpcport = None + + self.rpcport = rpcport + self.prefix = 'lss' + + if not os.path.exists(lss_dir): + os.makedirs(lss_dir) + + self.cmd_line = [ + 'lssd', + '--datadir={}'.format(lss_dir), + '--port={}'.format(rpcport), + ] + + def __del__(self): + if self.reserved_rpcport is not None: + drop_unused_port(self.reserved_rpcport) + + def start(self): + self.env['RUST_LOG'] = 'debug' + TailableProc.start(self) + self.wait_for_log("ready on", timeout=TIMEOUT) + + logging.info("LssD started") + + def stop(self): + logging.info("Stopping LssD") + return TailableProc.stop(self) + + class BitcoinD(TailableProc): def __init__(self, bitcoin_dir="/tmp/bitcoind-test", rpcport=None): @@ -578,11 +618,49 @@ def getnewaddress(self): return info['unconfidential'] +class ValidatingLightningSignerD(TailableProc): + def __init__(self, vlsd_dir, vlsd_port, node_id, network): + TailableProc.__init__(self, vlsd_dir, verbose=True) + self.executable = env("REMOTE_SIGNER_CMD", 'vlsd2') + os.environ['ALLOWLIST'] = env( + 'REMOTE_SIGNER_ALLOWLIST', + 'contrib/remote_hsmd/TESTING_ALLOWLIST') + self.opts = [ + '--network={}'.format(network), + '--datadir={}'.format(vlsd_dir), + '--connect=http://localhost:{}'.format(vlsd_port), + '--integration-test', + ] + self.prefix = 'vlsd2-%d' % (node_id) + self.vlsd_port = vlsd_port + + @property + def cmd_line(self): + return [self.executable] + self.opts + + def start(self, stdin=None, stdout_redir=True, stderr_redir=True, + wait_for_initialized=True): + TailableProc.start(self, stdin, stdout_redir, stderr_redir) + # We need to always wait for initialization + self.wait_for_log("vlsd2 git_desc") + logging.info("vlsd2 started") + + def stop(self, timeout=10): + logging.info("stopping vlsd2") + rc = TailableProc.stop(self, timeout) + logging.info("vlsd2 stopped") + self.logs_catchup() + return rc + + def __del__(self): + self.logs_catchup() + class LightningD(TailableProc): def __init__( self, lightning_dir, bitcoindproxy, + lssd_port, port=9735, random_hsm=False, node_id=0, @@ -594,9 +672,16 @@ def __init__( self.lightning_dir = lightning_dir self.port = port self.cmd_prefix = [] + self.lightning_dir = lightning_dir + self.use_vlsd = False + self.vlsd_dir = os.path.join(lightning_dir, "vlsd") + self.vlsd_port = None + self.vlsd = None + self.node_id = node_id self.rpcproxy = bitcoindproxy self.env['CLN_PLUGIN_LOG'] = "cln_plugin=trace,cln_rpc=trace,cln_grpc=trace,debug" + self.lssd_port = lssd_port self.opts = LIGHTNINGD_CONFIG.copy() opts = { @@ -616,12 +701,38 @@ def __init__( if grpc_port is not None: opts['grpc-port'] = grpc_port + if SUBDAEMON: + assert node_id > 0 + subdaemons = SUBDAEMON.split(',') + # VLS_SERIAL_SELECT "swaps" the selected item with the first item + select = env("VLS_SERIAL_SELECT", '1') + if node_id == int(select): + ndx = 1 + elif node_id == 1: + ndx = int(select) + else: + ndx = node_id + if ndx > len(subdaemons): + # use the last element if not as many specifiers as nodes + opts['subdaemon'] = subdaemons[-1] + else: + # use the matching specifier + opts['subdaemon'] = subdaemons[ndx - 1] + + print(f"starting node {node_id} with subdaemon {opts['subdaemon']}") + if SUBDAEMON == 'hsmd:remote_hsmd_socket': + self.use_vlsd = True + for k, v in opts.items(): self.opts[k] = v if not os.path.exists(os.path.join(lightning_dir, TEST_NETWORK)): os.makedirs(os.path.join(lightning_dir, TEST_NETWORK)) + if self.use_vlsd: + if not os.path.exists(self.vlsd_dir): + os.makedirs(self.vlsd_dir) + # Last 32-bytes of final part of dir -> seed. seed = (bytes(re.search('([^/]+)/*$', lightning_dir).group(1), encoding='utf-8') + bytes(32))[:32] if not random_hsm: @@ -637,6 +748,10 @@ def __init__( self.early_opts = ['--developer'] def cleanup(self): + if self.use_vlsd: + # Make sure the remotesigner is shutdown + self.vlsd.stop() + # To force blackhole to exit, disconnect file must be truncated! if 'dev-disconnect' in self.opts: with open(self.opts['dev-disconnect'], "w") as f: @@ -657,12 +772,65 @@ def cmd_line(self): return self.cmd_prefix + [self.executable] + self.early_opts + opts + def __del__(self): + if self.vlsd_port is not None: + drop_unused_port(self.vlsd_port) + def start(self, stdin=None, wait_for_initialized=True, stderr_redir=False): - self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport - TailableProc.start(self, stdin, stdout_redir=False, stderr_redir=stderr_redir) - if wait_for_initialized: - self.wait_for_log("Server started with public key") - logging.info("LightningD started") + try: + self.env['VLS_LSS'] = f"http://localhost:{self.lssd_port}" + self.env['RUST_LOG'] = 'debug' + # Some of the remote hsmd proxies need a bitcoind RPC connection + self.env['BITCOIND_RPC_URL'] = 'http://{}:{}@localhost:{}'.format( + BITCOIND_CONFIG['rpcuser'], + BITCOIND_CONFIG['rpcpassword'], + BITCOIND_CONFIG['rpcport']) + + # The remote hsmd proxies need to know which network we are using + if 'network' in self.opts: + self.env['VLS_NETWORK'] = self.opts['network'] + + self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport + + if self.use_vlsd: + self.vlsd_port = reserve_unused_port() + # We can't do this in the constructor because we need a new port on each restart. + self.env['VLS_PORT'] = str(self.vlsd_port) + # Kill any previous vlsd (we may have been restarted) + if self.vlsd is not None: + logging.info("killing prior vlsd") + self.vlsd.kill() + + TailableProc.start(self, stdin, stdout_redir=False, stderr_redir=stderr_redir) + + if self.use_vlsd: + # Start the remote signer first + self.vlsd = ValidatingLightningSignerD( + self.vlsd_dir, self.vlsd_port, self.node_id, self.opts['network']) + self.vlsd.start( + stdin, stdout_redir=True, stderr_redir=True, + wait_for_initialized=wait_for_initialized) + + if wait_for_initialized: + self.wait_for_log("Server started with public key") + logging.info("LightningD started") + except Exception: + if self.use_vlsd: + # LightningD didn't start, stop the remotesigner + self.vlsd.stop() + raise + + def stop(self, timeout=10): + if self.use_vlsd: + # Stop the remote signer first + self.vlsd.stop(timeout) + return TailableProc.stop(self, timeout) + + def kill(self): + if self.use_vlsd: + # Kill the remote signer first + self.vlsd.kill() + TailableProc.kill(self) def wait(self, timeout=TIMEOUT): """Wait for the daemon to stop for up to timeout seconds @@ -745,7 +913,7 @@ def call(self, method, payload=None, cmdprefix=None, filter=None): class LightningNode(object): - def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fail=False, + def __init__(self, node_id, lightning_dir, bitcoind, lssd, executor, valgrind, may_fail=False, may_reconnect=False, allow_broken_log=False, allow_warning=False, @@ -755,6 +923,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai valgrind_plugins=True, **kwargs): self.bitcoin = bitcoind + self.lssd = lssd self.executor = executor self.may_fail = may_fail self.may_reconnect = may_reconnect @@ -774,6 +943,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai self.daemon = LightningD( lightning_dir, bitcoindproxy=bitcoind.get_proxy(), + lssd_port=lssd.rpcport, port=port, random_hsm=random_hsm, node_id=node_id, grpc_port=self.grpc_port, ) @@ -1222,6 +1392,9 @@ def pay(self, dst, amt, label=None, route=False): 'channel': scid } + # let the signer know this payment is coming + self.rpc.preapproveinvoice(bolt11=inv['bolt11']) + # sendpay is async now self.rpc.sendpay([routestep], rhash, payment_secret=psecret, bolt11=inv['bolt11']) # wait for sendpay to comply @@ -1478,7 +1651,7 @@ def flock(directory: Path): class NodeFactory(object): """A factory to setup and start `lightningd` daemons. """ - def __init__(self, request, testname, bitcoind, executor, directory, + def __init__(self, request, testname, bitcoind, lssd, executor, directory, db_provider, node_cls, jsonschemas): if request.node.get_closest_marker("slow_test") and SLOW_MACHINE: self.valgrind = False @@ -1490,6 +1663,7 @@ def __init__(self, request, testname, bitcoind, executor, directory, self.reserved_ports = [] self.executor = executor self.bitcoind = bitcoind + self.lssd = lssd self.directory = directory self.lock = threading.Lock() self.db_provider = db_provider @@ -1575,7 +1749,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, db = self.db_provider.get_db(os.path.join(lightning_dir, TEST_NETWORK), self.testname, node_id) db.provider = self.db_provider node = self.node_cls( - node_id, lightning_dir, self.bitcoind, self.executor, self.valgrind, db=db, + node_id, lightning_dir, self.bitcoind, self.lssd, self.executor, self.valgrind, db=db, port=port, options=options, may_fail=may_fail or expect_fail, jsonschemas=self.jsonschemas, **kwargs diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index 9c798e6a289e..b30ef42f82fc 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -21,6 +21,7 @@ pyln-client = ">=23" Flask = "^2" cheroot = "^8" psutil = "^5.9" +requests = "^2.31.0" grpcio = { version = "^1", optional = true } pyln-grpc-proto = { version = "^0.1", optional = true } diff --git a/doc/contribute-to-core-lightning/contributor-workflow.md b/doc/contribute-to-core-lightning/contributor-workflow.md index 90ab20c4a3d0..81fe403d6cb5 100644 --- a/doc/contribute-to-core-lightning/contributor-workflow.md +++ b/doc/contribute-to-core-lightning/contributor-workflow.md @@ -110,7 +110,7 @@ Here's a checklist for the release process. 2. Use `devtools/changelog.py` to collect the changelog entries from pull request commit messages and merge them into the manually maintained `CHANGELOG.md`. This does API queries to GitHub, which are severely ratelimited unless you use an API token: set the `GH_TOKEN` environment variable to a Personal Access Token from 3. Create a new CHANGELOG.md heading to `vrc1`, and create a link at the bottom. Note that you should exactly copy the date and name format from a previous release, as the `build-release.sh` script relies on this. -4. Update the contrib/pyln package versions: `make update-pyln-versions NEW_VERSION=` +4. Update the contrib/pyln package versions: `make update-py-versions NEW_VERSION=` 5. Create a PR with the above. ### Releasing -rc1 @@ -130,7 +130,7 @@ Here's a checklist for the release process. ### Releasing -rc2, ..., -rcN 1. Change rc(N-1) to rcN in CHANGELOG.md. -2. Update the contrib/pyln package versions: `make update-pyln-versions NEW_VERSION=` +2. Update the contrib/pyln package versions: `make update-py-versions NEW_VERSION=` 3. Add a PR with the rcN. 4. Tag it `git pull && git tag -s vrcN && git push --tags` 5. Announce tagged rc release on core-lightning's release-chat channel on Discord & [BuildOnL2](https://community.corelightning.org/c/general-questions/). diff --git a/doc/developers-guide/app-development/grpc.md b/doc/developers-guide/app-development/grpc.md index 76e9bb3dc11e..2a9ccfaecca4 100644 --- a/doc/developers-guide/app-development/grpc.md +++ b/doc/developers-guide/app-development/grpc.md @@ -5,11 +5,11 @@ hidden: false createdAt: "2023-02-07T12:52:39.665Z" updatedAt: "2023-02-08T09:56:41.158Z" --- -> 📘 -> +> 📘 +> > Used for applications that want to connect to CLN over the network in a secure manner. -Since v0.11.0, Core Lightning provides a new interface: `cln-grpc`, a Rust-based plugin that provides a standardized API that apps, plugins, and other tools could use to interact with Core Lightning securely. +Since v0.11.0, Core Lightning provides a new interface: `cln-grpc`, a Rust-based plugin that provides a standardized API that apps, plugins, and other tools could use to interact with Core Lightning securely. We always had a JSON-RPC, with a very exhaustive API, but it was exposed only locally over a Unix-domain socket. Some plugins chose to re-expose the API over a variety of protocols, ranging from REST to gRPC, but it was additional work to install them. The gRPC API is automatically generated from our existing JSON-RPC API, so it has the same low-level and high-level access that app devs are accustomed to but uses a more efficient binary encoding where possible and is secured via mutual TLS authentication. @@ -54,13 +54,23 @@ python -m grpc_tools.protoc \ --experimental_allow_proto3_optional ``` - - This will generate two files in the current directory: - `node_pb2.py`: the description of the protobuf messages we'll be exchanging with the server. - `node_pb2_grpc.py`: the service and method stubs representing the server-side methods as local objects and associated methods. +Finally, we generate the file `primitives_pb2.py` that contains +protobuf messages imported in `node_pb2.py` file by running the +following command: + +```bash +python -m grpc_tools.protoc \ + -I lightning/cln-grpc/proto \ + path/to/cln-grpc/proto/primitives.proto \ + --python_out=. \ + --experimental_allow_proto3_optional +``` + ### Connecting to the node Finally we can use the generated stubs and mTLS identity to connect to the node: @@ -69,6 +79,7 @@ Finally we can use the generated stubs and mTLS identity to connect to the node: from pathlib import Path from node_pb2_grpc import NodeStub import node_pb2 +import grpc p = Path(".") cert_path = p / "client.pem" @@ -82,7 +93,7 @@ creds = grpc.ssl_channel_credentials( ) channel = grpc.secure_channel( - f"localhost:{grpc_port}", + "localhost:", creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) @@ -91,7 +102,8 @@ stub = NodeStub(channel) print(stub.Getinfo(node_pb2.GetinfoRequest())) ``` - +Note that we must replace `` by the corresponding port we +used as `--grpc-port` option when we started our node. In this example, we first load the client identity as well as the CA certificate so we can verify the server's identity against it. We then create a `creds` instance using those details. Next we open a secure channel, i.e., a channel over TLS with verification of identities. @@ -128,4 +140,4 @@ openssl x509 -req -CA ca.pem -CAkey ca-key.pem \ -This will finally create the `server.pem` file, signed by the CA, allowing you to access the node through its real domain name. You can now move `server.pem` and `server-key.pem` into the lightning directory, and they should be picked up during the start. \ No newline at end of file +This will finally create the `server.pem` file, signed by the CA, allowing you to access the node through its real domain name. You can now move `server.pem` and `server-key.pem` into the lightning directory, and they should be picked up during the start. diff --git a/doc/developers-guide/app-development/rest.md b/doc/developers-guide/app-development/rest.md index 4744938e0e63..3bb321ddf47e 100644 --- a/doc/developers-guide/app-development/rest.md +++ b/doc/developers-guide/app-development/rest.md @@ -28,7 +28,7 @@ An online demo for the REST interface is available at [REST API REFERENCE](ref:g > > - The `ip` should be configured with your system's public IP address. > -> - Default `rest-host` is `127.0.0.1` but this testing will require it to be `0.0.0.0`. +> - Default `clnrest-host` is `127.0.0.1` but this testing will require it to be `0.0.0.0`. > > Note: This setup is for **testing only**. It is **highly recommended** to test with _non-mainnet_ (regtest/testnet) setup only. @@ -38,36 +38,37 @@ An online demo for the REST interface is available at [REST API REFERENCE](ref:g The plugin is built-in with Core Lightning but its python dependencies are not, and must be installed separately. Install required packages with `pip install -r plugins/clnrest/requirements.txt`. -Note: if you have the older c-lightning-rest plugin, you can configure Core Lightning with `disable-plugin=clnrest.py` option -to avoid any conflict with this one. Of course, you could use this one instead! +Note: if you have the older c-lightning-REST plugin, you can configure Core Lightning with `disable-plugin=clnrest.py` +option to avoid confusion with this one. You can also run both plugins simultaneously till all your applications +are not migrated to `clnrest`. ## Configuration -If `rest-port` is not specified, the plugin will disable itself. +If `clnrest-port` is not specified, the plugin will disable itself. -- --rest-port: Sets the REST server port to listen to (3010 is common) +- --clnrest-port: Sets the REST server port to listen to (3010 is common) -- --rest-protocol: Specifies the REST server protocol. Default is HTTPS. +- --clnrest-protocol: Specifies the REST server protocol. Default is HTTPS. -- --rest-host: Defines the REST server host. Default is 127.0.0.1. +- --clnrest-host: Defines the REST server host. Default is 127.0.0.1. -- --rest-certs: Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate. +- --clnrest-certs: Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate. If it is missing at the configured location, new identity will be generated. -- --rest-csp: Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks. +- --clnrest-csp: Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks. Default CSP: `default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';` Example CSP: -`rest-csp=default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';`. +`clnrest-csp=default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';`. -- --rest-cors-origins: Define multiple origins which are allowed to share resources on web pages to a domain different from the +- --clnrest-cors-origins: Define multiple origins which are allowed to share resources on web pages to a domain different from the one that served the web page. Default is `*` which allows all origins. Example to define multiple origins: ``` -rest-cors-origins=https://localhost:5500 -rest-cors-origins=http://192.168.1.50:3030 -rest-cors-origins=https?://127.0.0.1:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]) +clnrest-cors-origins=https://localhost:5500 +clnrest-cors-origins=http://192.168.1.50:3030 +clnrest-cors-origins=https?://127.0.0.1:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]) ``` diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index ee6544cbbdc4..e44918a0de3a 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -15,7 +15,9 @@ The **decode** RPC command checks and parses: or `LIGHTNING:`) as specified by the BOLT 11 and BOLT 12 specifications. - a *rune* as created by lightning-commando-rune(7). -- a *emergency\_recover* starting with clnemerg1 which contains the content of emergency.recover file. +- an *emergency\_recover* string generated by hsmtool like + `lightning-hsmtool getemergencyrecover `. + It holds `emergency.recover` contents and starts with `clnemerg1`. It may decode other formats in future. diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 4347d9c11dff..8a039ac1d789 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -632,27 +632,27 @@ all DNS lookups, to avoid leaking information. Set a Tor control password, which may be needed for *autotor:* to authenticate to the Tor control port. -* **rest-port**=*PORT* [plugin `clnrest.py`] +* **clnrest-port**=*PORT* [plugin `clnrest.py`] Sets the REST server port to listen to (3010 is common). If this is not specified, the clnrest.py plugin will be disabled. -* **rest-protocol**=*PROTOCOL* [plugin `clnrest.py`] +* **clnrest-protocol**=*PROTOCOL* [plugin `clnrest.py`] Specifies the REST server protocol. Default is HTTPS. -* **rest-host**=*HOST* [plugin `clnrest.py`] +* **clnrest-host**=*HOST* [plugin `clnrest.py`] Defines the REST server host. Default is 127.0.0.1. -* **rest-certs**=*PATH* [plugin `clnrest.py`] +* **clnrest-certs**=*PATH* [plugin `clnrest.py`] Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate. If it is missing at the configured location, new identity (`client.pem` and `client-key.pem`) will be generated. -* **rest-cors-origins**=*CORSORIGINS* [plugin `clnrest.py`] +* **clnrest-cors-origins**=*CORSORIGINS* [plugin `clnrest.py`] Define multiple origins which are allowed to share resources on web pages to a domain different from the one that served the web page. Default is `*` which allows all origins. -* **rest-csp**=*CSPOLICY* [plugin `clnrest.py`] +* **clnrest-csp**=*CSPOLICY* [plugin `clnrest.py`] Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks. Default CSP is `default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';`. diff --git a/doc/schemas/fetchinvoice.request.json b/doc/schemas/fetchinvoice.request.json new file mode 100644 index 000000000000..d3f293d4ab61 --- /dev/null +++ b/doc/schemas/fetchinvoice.request.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "offer" + ], + "additionalProperties": false, + "properties": { + "offer": { + "type": "string", + "description": "" + }, + "amount_msat": { + "type": "msat", + "description": "amount_msat is required if the offer does not specify an amount at all, otherwise it is optional (but presumably if you set it to less than the offer, you will get an error from the issuer)." + }, + "quantity": { + "type": "u64", + "description": "quantity is is required if the offer specifies quantity_max, otherwise it is not allowed." + }, + "recurrence_counter": { + "type": "u64", + "description": "recurrence_counter is required if the offer specifies recurrence, otherwise it is not allowed. recurrence_counter should first be set to 0, and incremented for each successive invoice in a given series." + }, + "recurrence_start": { + "type": "number", + "description": "recurrence_start is required if the offer specifies recurrence_base with start_any_period set, otherwise it is not allowed. It indicates what period number to start at." + }, + "recurrence_label": { + "type": "string", + "description": "recurrence_label is required if recurrence_counter is set, and otherwise is not allowed. It must be the same as prior fetchinvoice calls for the same recurrence, as it is used to link them together." + }, + "timeout": { + "type": "number", + "description": "timeout is an optional timeout; if we don't get a reply before this we fail (default, 60 seconds)." + }, + "payer_note": { + "type": "string", + "description": "payer_note is an optional payer note to ask the issuer to include in the fetched invoice." + } + } +} diff --git a/lightningd/.gitignore b/lightningd/.gitignore index 4ba7a1a9cec2..3b7704593aa8 100644 --- a/lightningd/.gitignore +++ b/lightningd/.gitignore @@ -1,4 +1,4 @@ -lightningd +/remote_hsmd_vls lightning_channeld lightning_closingd lightning_connectd @@ -8,3 +8,8 @@ lightning_hsmd lightning_onchaind lightning_openingd lightning_websocketd +lightningd +remote_hsmd +remote_hsmd_inplace +remote_hsmd_serial +remote_hsmd_socket diff --git a/lightningd/channel.c b/lightningd/channel.c index 6a226985474d..a20b3f5387c9 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -150,7 +150,8 @@ new_inflight(struct channel *channel, const struct amount_msat lease_fee, const struct amount_sat lease_amt, s64 splice_amnt, - bool i_am_initiator) + bool i_am_initiator, + bool force_sign_first) { struct channel_inflight *inflight = tal(channel, struct channel_inflight); @@ -183,6 +184,7 @@ new_inflight(struct channel *channel, inflight->lease_amt = lease_amt; inflight->i_am_initiator = i_am_initiator; + inflight->force_sign_first = force_sign_first; inflight->splice_locked_memonly = false; list_add_tail(&channel->inflights, &inflight->list); diff --git a/lightningd/channel.h b/lightningd/channel.h index ea58bf09046d..1df9bd032793 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -76,6 +76,9 @@ struct channel_inflight { /* Did I initate this splice attempt? */ bool i_am_initiator; + /* On reestablish recovery; should I sign first? */ + bool force_sign_first; + /* Note: This field is not stored in the database. * * After splice_locked, we need a way to stop the chain watchers from @@ -397,7 +400,8 @@ struct channel_inflight *new_inflight(struct channel *channel, const struct amount_msat lease_fee, const struct amount_sat lease_amt, s64 splice_amnt, - bool i_am_initiator); + bool i_am_initiator, + bool force_sign_first); /* Add a last_tx and sig to an inflight */ void inflight_set_last_tx(struct channel_inflight *inflight, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index f39f211457e9..7018e63457aa 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -499,9 +499,7 @@ static void send_splice_tx(struct channel *channel, 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: */ +/* After channeld have all the signatures it sends the result to us here */ static void handle_splice_confirmed_signed(struct lightningd *ld, struct channel *channel, const u8 *msg) @@ -512,7 +510,8 @@ static void handle_splice_confirmed_signed(struct lightningd *ld, struct channel_inflight *inflight; u32 output_index; - if (!fromwire_channeld_splice_confirmed_signed(tmpctx, msg, &tx, &output_index)) { + if (!fromwire_channeld_splice_confirmed_signed(tmpctx, msg, &tx, + &output_index)) { channel_internal_error(channel, "bad splice_confirmed_signed %s", @@ -532,23 +531,130 @@ static void handle_splice_confirmed_signed(struct lightningd *ld, inflight->remote_tx_sigs = true; wallet_inflight_save(ld->wallet, inflight); - if (channel->state != CHANNELD_NORMAL) { + if (channel->state != CHANNELD_AWAITING_SPLICE) { log_debug(channel->log, "Would broadcast splice, but state %s" - " isn't CHANNELD_NORMAL", + " isn't CHANNELD_AWAITING_SPLICE", channel_state_name(channel)); return; } + cc = splice_command_for_chan(ld, channel); + + send_splice_tx(channel, tx, cc, output_index); +} + +static enum watch_result splice_depth_cb(struct lightningd *ld, + const struct bitcoin_txid *txid, + const struct bitcoin_tx *tx, + unsigned int depth, + void *param) +{ + /* find_txwatch triggers a type warning on inflight, so we do this. */ + struct channel_inflight *inflight = param; + struct txlocator *loc; + struct short_channel_id scid; + + /* What scid is this giving us? */ + loc = wallet_transaction_locate(tmpctx, ld->wallet, txid); + if (!mk_short_channel_id(&scid, + loc->blkheight, loc->index, + inflight->funding->outpoint.n)) { + channel_fail_permanent(inflight->channel, + REASON_LOCAL, + "Invalid funding scid %u:%u:%u", + loc->blkheight, loc->index, + inflight->funding->outpoint.n); + return false; + } + + /* Usually, we're here because we're awaiting a splice, but + * we could also mutual shutdown, or that weird splice_locked_memonly + * hack... */ + if (inflight->channel->state != CHANNELD_AWAITING_SPLICE) { + log_info(inflight->channel->log, "Splice inflight event but not" + " in AWAITING_SPLICE, ending watch of txid %s", + type_to_string(tmpctx, struct bitcoin_txid, txid)); + return DELETE_WATCH; + } + + /* Reorged out? OK, we're not committed yet. */ + if (depth == 0) { + return KEEP_WATCHING; + } + + if (inflight->channel->owner) { + log_info(inflight->channel->log, "splice_depth_cb: sending funding depth scid: %s", + type_to_string(tmpctx, struct short_channel_id, &scid)); + subd_send_msg(inflight->channel->owner, + take(towire_channeld_funding_depth( + NULL, &scid, + inflight->channel->alias[LOCAL], + depth, true, txid))); + } + + /* channeld will tell us when splice is locked in: we'll clean + * this watch up then. */ + return KEEP_WATCHING; +} + +void watch_splice_inflight(struct lightningd *ld, + struct channel_inflight *inflight) +{ + log_info(inflight->channel->log, "Watching splice inflight %s", + type_to_string(tmpctx, struct bitcoin_txid, + &inflight->funding->outpoint.txid)); + watch_txid(inflight, ld->topology, + &inflight->funding->outpoint.txid, + splice_depth_cb, inflight); +} + +static struct txwatch *splice_inflight_txwatch(struct channel *channel, + struct channel_inflight *inflight) +{ + return find_txwatch(channel->peer->ld->topology, + &inflight->funding->outpoint.txid, + splice_depth_cb, channel); +} + +static void handle_splice_sending_sigs(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct splice_command *cc; + struct bitcoin_txid txid; + struct channel_inflight *inflight; + + if (!fromwire_channeld_splice_sending_sigs(msg, &txid)) { + + channel_internal_error(channel, + "bad splice_confirmed_signed %s", + tal_hex(channel, msg)); + return; + } + + 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)); + + /* Signing a splice after it has confirmed is safe and can happen during + * reestablish if one node is late seeing blocks */ + if (channel->state == CHANNELD_AWAITING_SPLICE) + 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"); + "Splice signatures sent"); - send_splice_tx(channel, tx, cc, output_index); + watch_splice_inflight(ld, inflight); } bool depthcb_update_scid(struct channel *channel, @@ -600,46 +706,6 @@ bool depthcb_update_scid(struct channel *channel, return true; } -static enum watch_result splice_depth_cb(struct lightningd *ld, - const struct bitcoin_txid *txid, - const struct bitcoin_tx *tx, - unsigned int depth, - struct channel_inflight *inflight) -{ - /* Usually, we're here because we're awaiting a splice, but - * we could also mutual shutdown, or that weird splice_locked_memonly - * hack... */ - if (inflight->channel->state != CHANNELD_AWAITING_SPLICE) - return DELETE_WATCH; - - /* Reorged out? OK, we're not committed yet. */ - if (depth == 0) - return KEEP_WATCHING; - - if (!depthcb_update_scid(inflight->channel, txid, &inflight->funding->outpoint)) - return DELETE_WATCH; - - if (inflight->channel->owner) { - subd_send_msg(inflight->channel->owner, - take(towire_channeld_funding_depth( - NULL, inflight->channel->scid, - inflight->channel->alias[LOCAL], - depth, true, txid))); - } - - /* channeld will tell us when splice is locked in: we'll clean - * this watch up then. */ - return KEEP_WATCHING; -} - -void watch_splice_inflight(struct lightningd *ld, - struct channel_inflight *inflight) -{ - watch_txid(inflight, ld->topology, - &inflight->funding->outpoint.txid, - splice_depth_cb, inflight); -} - static void handle_add_inflight(struct lightningd *ld, struct channel *channel, const u8 *msg) @@ -650,7 +716,7 @@ static void handle_add_inflight(struct lightningd *ld, s64 splice_amnt; struct wally_psbt *psbt; struct channel_inflight *inflight; - bool i_am_initiator; + bool i_am_initiator, force_sign_first; if (!fromwire_channeld_add_inflight(tmpctx, msg, @@ -660,7 +726,8 @@ static void handle_add_inflight(struct lightningd *ld, &satoshis, &splice_amnt, &psbt, - &i_am_initiator)) { + &i_am_initiator, + &force_sign_first)) { channel_internal_error(channel, "bad channel_add_inflight %s", tal_hex(channel, msg)); @@ -681,14 +748,14 @@ static void handle_add_inflight(struct lightningd *ld, AMOUNT_MSAT(0), AMOUNT_SAT(0), splice_amnt, - i_am_initiator); + i_am_initiator, + force_sign_first); 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); - watch_splice_inflight(ld, inflight); subd_send_msg(channel->owner, take(towire_channeld_got_inflight(NULL))); } @@ -885,6 +952,7 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) s64 splice_amnt; struct channel_inflight *inflight; struct bitcoin_txid locked_txid; + struct txwatch *txw; if (!fromwire_channeld_got_splice_locked(msg, &funding_sats, &splice_amnt, @@ -922,6 +990,9 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) wallet_channel_clear_inflights(channel->peer->ld->wallet, channel); + depthcb_update_scid(channel, &locked_txid, + &inflight->funding->outpoint); + /* That freed watchers in inflights: now watch funding tx */ channel_watch_funding(channel->peer->ld, channel); @@ -934,6 +1005,14 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) list_add_tail(&channel->inflights, &inflight->list); lockin_complete(channel, CHANNELD_AWAITING_SPLICE); + + /* Turn off tx watcher for the splice */ + txw = splice_inflight_txwatch(channel, inflight); + if (!txw) + log_unusual(channel->log, "Can't unwatch txid %s", + type_to_string(tmpctx, struct bitcoin_txid, + &locked_txid)); + tal_free(txw); } /* We were informed by channeld that channel is ready (reached mindepth) */ @@ -1318,6 +1397,9 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED: handle_splice_confirmed_signed(sd->ld, sd->channel, msg); break; + case WIRE_CHANNELD_SPLICE_SENDING_SIGS: + handle_splice_sending_sigs(sd->ld, sd->channel, msg); + break; case WIRE_CHANNELD_ADD_INFLIGHT: handle_add_inflight(sd->ld, sd->channel, msg); break; @@ -1524,6 +1606,7 @@ bool peer_start_channeld(struct channel *channel, infcopy->outpoint = inflight->funding->outpoint; infcopy->amnt = inflight->funding->total_funds; + infcopy->remote_tx_sigs = inflight->remote_tx_sigs; infcopy->splice_amnt = inflight->funding->splice_amnt; if (inflight->last_tx) infcopy->last_tx = tal_dup(infcopy, struct bitcoin_tx, inflight->last_tx); @@ -1531,9 +1614,12 @@ bool peer_start_channeld(struct channel *channel, infcopy->last_tx = NULL; infcopy->last_sig = inflight->last_sig; infcopy->i_am_initiator = inflight->i_am_initiator; + infcopy->force_sign_first = inflight->force_sign_first; + 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); } @@ -1643,7 +1729,7 @@ void channeld_tell_depth(struct channel *channel, subd_send_msg(channel->owner, take(towire_channeld_funding_depth( NULL, channel->scid, channel->alias[LOCAL], depth, - channel->state == CHANNELD_AWAITING_SPLICE, txid))); + false, txid))); } /* Check if we are the fundee of this channel, the channel diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 5702cb2dd1e3..5de6d1138336 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1292,6 +1292,7 @@ wallet_update_channel(struct lightningd *ld, channel->push, lease_amt, 0, + false, false); wallet_inflight_add(ld->wallet, inflight); @@ -1510,6 +1511,7 @@ wallet_commit_channel(struct lightningd *ld, channel->push, lease_amt, 0, + false, false); wallet_inflight_add(ld->wallet, inflight); diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 889ea87364ef..8d1b6c536a7e 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -167,7 +167,7 @@ struct ext_key *hsm_init(struct lightningd *ld) } if (feature_offered(ld->our_features->bits[INIT_FEATURE], - WIRE_HSMD_SIGN_SPLICE_TX) + OPT_EXPERIMENTAL_SPLICE) && !hsm_capable(ld, WIRE_HSMD_SIGN_SPLICE_TX)) { fatal("--experimental-splicing needs HSM capable of signing splices!"); } diff --git a/plugins/clnrest/Makefile b/plugins/clnrest/Makefile new file mode 100644 index 000000000000..38aa45d662ef --- /dev/null +++ b/plugins/clnrest/Makefile @@ -0,0 +1,3 @@ +upgrade-version: + if [ -z "$(NEW_VERSION)" ]; then echo "Set NEW_VERSION!" >&2; exit 1; fi + poetry version $(NEW_VERSION) \ No newline at end of file diff --git a/plugins/clnrest/clnrest.py b/plugins/clnrest/clnrest.py index 5dc66cc0826d..9da124e513d3 100755 --- a/plugins/clnrest/clnrest.py +++ b/plugins/clnrest/clnrest.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 # For --hidden-import gunicorn.glogging gunicorn.workers.sync try: + from gevent import monkey + monkey.patch_ssl() import sys import os import re @@ -117,7 +119,7 @@ def add_csp_headers(response): response.headers['Content-Security-Policy'] = REST_CSP.replace('\\', '').replace("[\"", '').replace("\"]", '') return response except Exception as err: - plugin.log(f"Error from rest-csp config: {err}", "info") + plugin.log(f"Error from clnrest-csp config: {err}", "info") def set_application_options(plugin): diff --git a/plugins/clnrest/utilities/rpc_plugin.py b/plugins/clnrest/utilities/rpc_plugin.py index 3266d2ff01e5..fbb0019b324b 100644 --- a/plugins/clnrest/utilities/rpc_plugin.py +++ b/plugins/clnrest/utilities/rpc_plugin.py @@ -3,9 +3,9 @@ plugin = Plugin(autopatch=False) -plugin.add_option(name="rest-certs", default=os.getcwd(), description="Path for certificates (for https)", opt_type="string", deprecated=False) -plugin.add_option(name="rest-protocol", default="https", description="REST server protocol", opt_type="string", deprecated=False) -plugin.add_option(name="rest-host", default="127.0.0.1", description="REST server host", opt_type="string", deprecated=False) -plugin.add_option(name="rest-port", default=None, description="REST server port to listen", opt_type="int", deprecated=False) -plugin.add_option(name="rest-cors-origins", default="*", description="Cross origin resource sharing origins", opt_type="string", deprecated=False, multi=True) -plugin.add_option(name="rest-csp", default="default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';", description="Content security policy (CSP) for the server", opt_type="string", deprecated=False, multi=False) +plugin.add_option(name="clnrest-certs", default=os.getcwd(), description="Path for certificates (for https)", opt_type="string", deprecated=False) +plugin.add_option(name="clnrest-protocol", default="https", description="REST server protocol", opt_type="string", deprecated=False) +plugin.add_option(name="clnrest-host", default="127.0.0.1", description="REST server host", opt_type="string", deprecated=False) +plugin.add_option(name="clnrest-port", default=None, description="REST server port to listen", opt_type="int", deprecated=False) +plugin.add_option(name="clnrest-cors-origins", default="*", description="Cross origin resource sharing origins", opt_type="string", deprecated=False, multi=True) +plugin.add_option(name="clnrest-csp", default="default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';", description="Content security policy (CSP) for the server", opt_type="string", deprecated=False, multi=False) diff --git a/plugins/clnrest/utilities/rpc_routes.py b/plugins/clnrest/utilities/rpc_routes.py index 7b7d8333d18c..78a8dd9c2b4e 100644 --- a/plugins/clnrest/utilities/rpc_routes.py +++ b/plugins/clnrest/utilities/rpc_routes.py @@ -1,7 +1,8 @@ +from pyln.client import RpcError import json5 from flask import request, make_response from flask_restx import Namespace, Resource -from .shared import call_rpc_method, verify_rune, process_help_response +from .shared import call_rpc_method, verify_rune, process_help_response, RuneError from .rpc_plugin import plugin methods_list = [] @@ -42,20 +43,19 @@ def post(self, rpc_method): rpc_params = request.form.to_dict() if not request.is_json else request.get_json() if len(request.data) != 0 else {} try: - is_valid_rune = verify_rune(plugin, rune, rpc_method, rpc_params) - if "error" in is_valid_rune: - plugin.log(f"Error: {is_valid_rune}", "error") - raise Exception(is_valid_rune) - - except Exception as err: - return json5.loads(str(err)), 401 + verify_rune(plugin, rune, rpc_method, rpc_params) + except RpcError as rpc_err: + plugin.log(f"RPC Error: {str(rpc_err.error)}", "info") + return rpc_err.error, 401 + except RuneError as rune_err: + plugin.log(f"Rune Error: {str(rune_err)}", "info") + return rune_err.error, 403 try: return call_rpc_method(plugin, rpc_method, rpc_params), 201 - - except Exception as err: - plugin.log(f"Error: {err}", "info") - return json5.loads(str(err)), 500 + except RpcError as rpc_err: + plugin.log(f"RPC Error: {str(rpc_err.error)}", "info") + return (rpc_err.error, 404) if rpc_err.error["code"] == -32601 else (rpc_err.error, 500) except Exception as err: return f"Unable to parse request: {err}", 500 diff --git a/plugins/clnrest/utilities/shared.py b/plugins/clnrest/utilities/shared.py index eab00af3f11a..1f0acd569c47 100644 --- a/plugins/clnrest/utilities/shared.py +++ b/plugins/clnrest/utilities/shared.py @@ -1,12 +1,17 @@ import json5 -import re -import json import ipaddress +import pyln.client CERTS_PATH, REST_PROTOCOL, REST_HOST, REST_PORT, REST_CSP, REST_CORS_ORIGINS = "", "", "", "", "", [] +class RuneError(Exception): + def __init__(self, error=str({"code": 1501, "message": "Not authorized: Missing or invalid rune"})): + self.error = error + super().__init__(self.error) + + def validate_ip4(ip_str): try: # Create an IPv4 address object. @@ -34,25 +39,25 @@ def validate_port(port): def set_config(options): - if 'rest-port' not in options: - return "`rest-port` option is not configured" + if 'clnrest-port' not in options: + return "`clnrest-port` option is not configured" global CERTS_PATH, REST_PROTOCOL, REST_HOST, REST_PORT, REST_CSP, REST_CORS_ORIGINS - REST_PORT = int(options["rest-port"]) + REST_PORT = int(options["clnrest-port"]) if validate_port(REST_PORT) is False: - return f"`rest-port` {REST_PORT}, should be a valid available port between 1024 and 65535." + return f"`clnrest-port` {REST_PORT}, should be a valid available port between 1024 and 65535." - REST_HOST = str(options["rest-host"]) + REST_HOST = str(options["clnrest-host"]) if REST_HOST != "localhost" and validate_ip4(REST_HOST) is False and validate_ip6(REST_HOST) is False: - return f"`rest-host` should be a valid IP." + return f"`clnrest-host` should be a valid IP." - REST_PROTOCOL = str(options["rest-protocol"]) + REST_PROTOCOL = str(options["clnrest-protocol"]) if REST_PROTOCOL != "http" and REST_PROTOCOL != "https": - return f"`rest-protocol` can either be http or https." + return f"`clnrest-protocol` can either be http or https." - CERTS_PATH = str(options["rest-certs"]) - REST_CSP = str(options["rest-csp"]) - cors_origins = options["rest-cors-origins"] + CERTS_PATH = str(options["clnrest-certs"]) + REST_CSP = str(options["clnrest-csp"]) + cors_origins = options["clnrest-cors-origins"] REST_CORS_ORIGINS.clear() for origin in cors_origins: REST_CORS_ORIGINS.append(str(origin)) @@ -60,35 +65,34 @@ def set_config(options): return None +def convert_millisatoshis(item): + """ + The global JSON encoder has been replaced (see + monkey_patch_json!) by one that turns Millisatoshi class object + into strings ending in msat. We do not want the http response + to be encoded like that! pyln-client should probably not do that, + but meanwhile, convert them to integers. + """ + if isinstance(item, dict): + ret = {} + for k in item: + ret[k] = convert_millisatoshis(item[k]) + elif isinstance(item, list): + ret = [convert_millisatoshis(i) for i in item] + elif isinstance(item, pyln.client.Millisatoshi): + ret = int(item) + else: + ret = item + return ret + + def call_rpc_method(plugin, rpc_method, payload): - try: - response = plugin.rpc.call(rpc_method, payload) - if '"error":' in str(response).lower(): - raise Exception(response) - else: - plugin.log(f"{response}", "debug") - if '"result":' in str(response).lower(): - # Use json5.loads ONLY when necessary, as it increases processing time - return json.loads(response)["result"] - else: - return response - - except Exception as err: - plugin.log(f"Error: {err}", "info") - if "error" in str(err).lower(): - match_err_obj = re.search(r'"error":\{.*?\}', str(err)) - if match_err_obj is not None: - err = "{" + match_err_obj.group() + "}" - else: - match_err_str = re.search(r"error: \{.*?\}", str(err)) - if match_err_str is not None: - err = "{" + match_err_str.group() + "}" - raise Exception(err) + return convert_millisatoshis(plugin.rpc.call(rpc_method, payload)) def verify_rune(plugin, rune, rpc_method, rpc_params): if rune is None: - raise Exception('{ "error": {"code": 403, "message": "Not authorized: Missing rune"} }') + raise RuneError({"code": 1501, "message": "Not authorized: Missing rune"}) return call_rpc_method(plugin, "checkrune", {"rune": rune, diff --git a/poetry.lock b/poetry.lock index e28731edfd3f..70a2cd75c385 100644 --- a/poetry.lock +++ b/poetry.lock @@ -894,68 +894,73 @@ gevent = "*" [[package]] name = "greenlet" -version = "3.0.1" +version = "3.0.0" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"}, - {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"}, - {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"}, - {file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"}, - {file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"}, - {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"}, - {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"}, - {file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"}, - {file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"}, - {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"}, - {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"}, - {file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"}, - {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"}, - {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"}, - {file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"}, - {file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"}, - {file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"}, - {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"}, - {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"}, - {file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"}, - {file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"}, - {file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"}, - {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"}, - {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"}, - {file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"}, - {file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"}, - {file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"}, + {file = "greenlet-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a"}, + {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779"}, + {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9"}, + {file = "greenlet-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7"}, + {file = "greenlet-3.0.0-cp310-universal2-macosx_11_0_x86_64.whl", hash = "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f"}, + {file = "greenlet-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b"}, + {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c"}, + {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362"}, + {file = "greenlet-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c"}, + {file = "greenlet-3.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383"}, + {file = "greenlet-3.0.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb"}, + {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35"}, + {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17"}, + {file = "greenlet-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf94aa539e97a8411b5ea52fc6ccd8371be9550c4041011a091eb8b3ca1d810"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80dcd3c938cbcac986c5c92779db8e8ce51a89a849c135172c88ecbdc8c056b7"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52a712c38e5fb4fd68e00dc3caf00b60cb65634d50e32281a9d6431b33b4af1"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5539f6da3418c3dc002739cb2bb8d169056aa66e0c83f6bacae0cd3ac26b423"}, + {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:343675e0da2f3c69d3fb1e894ba0a1acf58f481f3b9372ce1eb465ef93cf6fed"}, + {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:abe1ef3d780de56defd0c77c5ba95e152f4e4c4e12d7e11dd8447d338b85a625"}, + {file = "greenlet-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:e693e759e172fa1c2c90d35dea4acbdd1d609b6936115d3739148d5e4cd11947"}, + {file = "greenlet-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705"}, + {file = "greenlet-3.0.0-cp37-universal2-macosx_11_0_x86_64.whl", hash = "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353"}, + {file = "greenlet-3.0.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b822fab253ac0f330ee807e7485769e3ac85d5eef827ca224feaaefa462dc0d0"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8060b32d8586e912a7b7dac2d15b28dbbd63a174ab32f5bc6d107a1c4143f40b"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb36985f606a7c49916eff74ab99399cdfd09241c375d5a820bb855dfb4af9f"}, + {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d"}, + {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f351479a6914fd81a55c8e68963609f792d9b067fb8a60a042c585a621e0de4f"}, + {file = "greenlet-3.0.0-cp38-cp38-win32.whl", hash = "sha256:9de687479faec7db5b198cc365bc34addd256b0028956501f4d4d5e9ca2e240a"}, + {file = "greenlet-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:3fd2b18432e7298fcbec3d39e1a0aa91ae9ea1c93356ec089421fabc3651572b"}, + {file = "greenlet-3.0.0-cp38-universal2-macosx_11_0_x86_64.whl", hash = "sha256:3c0d36f5adc6e6100aedbc976d7428a9f7194ea79911aa4bf471f44ee13a9464"}, + {file = "greenlet-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a"}, + {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692"}, + {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4"}, + {file = "greenlet-3.0.0-cp39-cp39-win32.whl", hash = "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9"}, + {file = "greenlet-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce"}, + {file = "greenlet-3.0.0-cp39-universal2-macosx_11_0_x86_64.whl", hash = "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355"}, + {file = "greenlet-3.0.0.tar.gz", hash = "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b"}, ] [package.extras] @@ -1749,6 +1754,7 @@ pyln-client = ">=23" pyln-grpc-proto = {version = "^0.1", optional = true} pytest = "^7" python-bitcoinlib = "^0.11.0" +requests = "^2.31.0" [package.extras] grpc = ["grpcio (>=1,<2)", "pyln-grpc-proto (>=0.1,<0.2)"] diff --git a/tests/fixtures.py b/tests/fixtures.py index ea23f10ad0a9..d9a6874080f6 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,5 +1,5 @@ from utils import TEST_NETWORK, VALGRIND # noqa: F401,F403 -from pyln.testing.fixtures import directory, test_base_dir, test_name, chainparams, node_factory, bitcoind, teardown_checks, db_provider, executor, setup_logging, jsonschemas # noqa: F401,F403 +from pyln.testing.fixtures import directory, test_base_dir, test_name, chainparams, node_factory, bitcoind, lssd, teardown_checks, db_provider, executor, setup_logging, jsonschemas # noqa: F401,F403 from pyln.testing import utils from utils import COMPAT from pathlib import Path diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 9614942e863d..fb58ce8fee0f 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -332,6 +332,7 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert len(fees) == 1 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "hsmd_sign_option_will_fund_offer not supported") @pytest.mark.openchannel('v2') @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") @unittest.skipIf(TEST_NETWORK != 'regtest', "network fees hardcoded") @@ -408,6 +409,7 @@ def _check_events(node, channel_id, exp_events): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") @unittest.skipIf(TEST_NETWORK != 'regtest', "network fees hardcoded") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "remote_hsmd doesn't allow push of non-trivial amount") @pytest.mark.openchannel('v1', 'Uses push-msat') def test_bookkeeping_missed_chans_pushed(node_factory, bitcoind): """ @@ -599,6 +601,7 @@ def test_bookkeeping_onchaind_txs(node_factory, bitcoind): assert outs == only_one(wallet_bal['balances'])['balance_msat'] +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "invoice from offer: Invalid bech32: invalid checksum") def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): """ When an 'invoice' type event comes through, we look up the description details diff --git a/tests/test_clnrest.py b/tests/test_clnrest.py new file mode 100644 index 000000000000..a9b3daea975a --- /dev/null +++ b/tests/test_clnrest.py @@ -0,0 +1,435 @@ +from ephemeral_port_reserve import reserve +from fixtures import * # noqa: F401,F403 +from pyln.testing.utils import env, TEST_NETWORK +from pyln.client import Millisatoshi +import unittest +import requests +from pathlib import Path +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry +import socketio +import time + + +def http_session_with_retry(): + # All requests are done after matching "REST server running" in the log, + # but there may be a 'small' lag between that message in the log and the + # web server really available for incoming requests. So we use an http + # session to retry several times the requests. + http_session = requests.Session() + retry = Retry(connect=10, backoff_factor=0.5) + adapter = HTTPAdapter(max_retries=retry) + http_session.mount('https://', adapter) + return http_session + + +def test_clnrest_no_auto_start(node_factory): + """Ensure that we do not start clnrest unless a `clnrest-port` is configured.""" + l1 = node_factory.get_node() + # This might happen really early! + l1.daemon.logsearch_start = 0 + assert [p for p in l1.rpc.plugin('list')['plugins'] if 'clnrest.py' in p['name']] == [] + assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-port` option is not configured') + + +def test_clnrest_self_signed_certificates(node_factory): + """Test that self-signed certificates have `clnrest-host` IP in Subject Alternative Name.""" + rest_port = str(reserve()) + rest_host = '127.0.0.1' + base_url = f'https://{rest_host}:{rest_port}' + l1 = node_factory.get_node(options={'disable-plugin': 'cln-grpc', + 'clnrest-port': rest_port, + 'clnrest-host': rest_host}) + # This might happen really early! + l1.daemon.logsearch_start = 0 + l1.daemon.wait_for_log(r'plugin-clnrest.py: REST server running at ' + base_url) + ca_cert = Path(l1.daemon.lightning_dir) / TEST_NETWORK / 'ca.pem' + + http_session = http_session_with_retry() + response = http_session.get(base_url + '/v1/list-methods', verify=ca_cert) + assert response.status_code == 200 + + +@unittest.skipIf(env('RUST') != '1', 'RUST is not enabled skipping rust-dependent tests') +def test_clnrest_uses_grpc_plugin_certificates(node_factory): + """Test that clnrest reuses `cln-grpc` plugin certificates if available. + Defaults: + - clnrest-protocol: https + """ + rest_host = 'localhost' + grpc_port = str(reserve()) + rest_port = str(reserve()) + l1 = node_factory.get_node(options={'grpc-port': grpc_port, 'clnrest-host': rest_host, 'clnrest-port': rest_port}) + base_url = f'https://{rest_host}:{rest_port}' + # This might happen really early! + l1.daemon.logsearch_start = 0 + l1.daemon.wait_for_logs([r'serving grpc on 0.0.0.0:', + r'plugin-clnrest.py: REST server running at ' + base_url]) + ca_cert = Path(l1.daemon.lightning_dir) / TEST_NETWORK / 'ca.pem' + http_session = http_session_with_retry() + response = http_session.get(base_url + '/v1/list-methods', verify=ca_cert) + assert response.status_code == 200 + + +def test_clnrest_generate_certificate(node_factory): + """Test whether we correctly generate the certificates.""" + # when `clnrest-protocol` is `http`, certs are not generated at `clnrest-certs` path + rest_port = str(reserve()) + rest_protocol = 'http' + rest_certs = node_factory.directory + '/clnrest-certs' + l1 = node_factory.get_node(options={'clnrest-port': rest_port, + 'clnrest-protocol': rest_protocol, + 'clnrest-certs': rest_certs}) + + assert not Path(rest_certs).exists() + + # node l1 not started + rest_port = str(reserve()) + rest_certs = node_factory.directory + '/clnrest-certs' + l1 = node_factory.get_node(options={'clnrest-port': rest_port, + 'clnrest-certs': rest_certs}, start=False) + rest_certs_path = Path(rest_certs) + files = [rest_certs_path / f for f in [ + 'ca.pem', + 'ca-key.pem', + 'client.pem', + 'client-key.pem', + 'server-key.pem', + 'server.pem', + ]] + + # before starting no files exist. + assert [f.exists() for f in files] == [False] * len(files) + + # certificates generated at startup + l1.start() + assert [f.exists() for f in files] == [True] * len(files) + + # the files exist, restarting should not change them + contents = [f.open().read() for f in files] + l1.restart() + assert contents == [f.open().read() for f in files] + + # remove client.pem file, so all certs are regenerated at restart + files[2].unlink() + l1.restart() + contents_1 = [f.open().read() for f in files] + assert [c[0] != c[1] for c in zip(contents, contents_1)] == [True] * len(files) + + # remove client-key.pem file, so all certs are regenerated at restart + files[3].unlink() + l1.restart() + contents_2 = [f.open().read() for f in files] + assert [c[0] != c[1] for c in zip(contents, contents_2)] == [True] * len(files) + + +def start_node_with_clnrest(node_factory): + """Start a node with the clnrest plugin, whose options are the default options. + Return: + - the node, + - the base url and + - the certificate authority path used for the self-signed certificates.""" + rest_port = str(reserve()) + rest_certs = node_factory.directory + '/clnrest-certs' + l1 = node_factory.get_node(options={'clnrest-port': rest_port, 'clnrest-certs': rest_certs}) + base_url = 'https://127.0.0.1:' + rest_port + # This might happen really early! + l1.daemon.logsearch_start = 0 + l1.daemon.wait_for_log(r'plugin-clnrest.py: REST server running at ' + base_url) + ca_cert = Path(rest_certs) / 'ca.pem' + return l1, base_url, ca_cert + + +def test_clnrest_list_methods(node_factory): + """Test GET request on `/v1/list-methods` end point with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + + # /v1/list-methods + http_session = http_session_with_retry() + response = http_session.get(base_url + '/v1/list-methods', verify=ca_cert) + assert response.status_code == 200 + assert response.text.find('Command: getinfo') > 0 + + +def test_clnrest_unknown_method(node_factory): + """Test GET request error on `/v1/unknown-get` end point with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + http_session = http_session_with_retry() + + response = http_session.get(base_url + '/v1/unknown-get', verify=ca_cert) + assert response.status_code == 405 + assert response.json()['message'] == 'The method is not allowed for the requested URL.' + + """Test POST request error on `/v1/unknown-post` end point.""" + rune = l1.rpc.createrune()['rune'] + response = http_session.post(base_url + '/v1/unknown-post', headers={'Rune': rune}, verify=ca_cert) + assert response.status_code == 404 + assert response.json()['code'] == -32601 + assert response.json()['message'] == "Unknown command 'unknown-post'" + + +def test_clnrest_rpc_method(node_factory): + """Test POST requests on `/v1/` end points with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + http_session = http_session_with_retry() + + # /v1/getinfo no rune provided in header of the request + response = http_session.post(base_url + '/v1/getinfo', verify=ca_cert) + assert response.status_code == 403 + assert response.json()['code'] == 1501 + assert response.json()['message'] == 'Not authorized: Missing rune' + + # /v1/getinfo with a rune which doesn't authorized getinfo method + rune_no_getinfo = l1.rpc.createrune(restrictions=[["method/getinfo"]])['rune'] + response = http_session.post(base_url + '/v1/getinfo', headers={'Rune': rune_no_getinfo}, + verify=ca_cert) + assert response.status_code == 401 + assert response.json()['code'] == 1502 + assert response.json()['message'] == 'Not permitted: method is equal to getinfo' + + # /v1/getinfo with a correct rune + rune_getinfo = l1.rpc.createrune(restrictions=[["method=getinfo"]])['rune'] + response = http_session.post(base_url + '/v1/getinfo', headers={'Rune': rune_getinfo}, + verify=ca_cert) + assert response.status_code == 201 + assert response.json()['id'] == l1.info['id'] + + # /v1/invoice with a correct rune but missing parameters + rune_invoice = l1.rpc.createrune(restrictions=[["method=invoice"]])['rune'] + response = http_session.post(base_url + '/v1/invoice', headers={'Rune': rune_invoice}, + verify=ca_cert) + assert response.status_code == 500 + assert response.json()['code'] == -32602 + + # /v1/invoice with a correct rune but wrong parameters + rune_invoice = l1.rpc.createrune(restrictions=[["method=invoice"]])['rune'] + response = http_session.post(base_url + '/v1/invoice', headers={'Rune': rune_invoice}, + data={'amount_msat': '', + 'label': 'label', + 'description': 'description'}, + verify=ca_cert) + assert response.status_code == 500 + assert response.json()['code'] == -32602 + + # l2 pays l1's invoice where the invoice is created with /v1/invoice + rune_invoice = l1.rpc.createrune(restrictions=[["method=invoice"]])['rune'] + response = http_session.post(base_url + '/v1/invoice', headers={'Rune': rune_invoice}, + data={'amount_msat': '50000000', + 'label': 'label', + 'description': 'description'}, + verify=ca_cert) + assert response.status_code == 201 + assert 'bolt11' in response.json() + + +def test_clnrest_large_response(node_factory): + """Test a large reply still works (and msat fields are integers!)""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + http_session = http_session_with_retry() + + # Add 500 invoices, test list + NUM_INVOICES = 500 + for i in range(NUM_INVOICES): + l1.rpc.invoice(amount_msat=100, label=str(i), description="inv") + + rune = l1.rpc.createrune()['rune'] + response = http_session.post(base_url + '/v1/listinvoices', headers={'Rune': rune}, + verify=ca_cert) + # No, this doesn't return JSON, it *parses* it into a Python object! + resp = response.json() + + # Make sure it hasn't turned msat fields into strings! + assert not isinstance(resp['invoices'][0]['amount_msat'], Millisatoshi) + assert len(resp['invoices']) == NUM_INVOICES + + +# Tests for websocket are written separately to avoid flake8 +# to complain with the errors F811 like this "F811 redefinition of +# unused 'message'". + +def notifications_received_via_websocket(l1, base_url, http_session): + """Return the list of notifications received by the websocket client. + + We try to connect to the websocket server running at `base_url` + with `http_session` parameters. Then we create an invoice on the node: + + - if we were effectively connected, we received an `invoice_creation` + notification via websocket that should be in the list of notifications + we return. + - if we couldn't connect to the websocket server, the notification list + we return is empty.""" + sio = socketio.Client(http_session=http_session) + notifications = [] + + @sio.event + def message(data): + notifications.append(data) + sio.connect(base_url) + time.sleep(2) + # trigger `invoice_creation` notification + l1.rpc.invoice(10000, "label", "description") + time.sleep(2) + sio.disconnect() + return notifications + + +def test_clnrest_websocket_no_rune(node_factory): + """Test websocket with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + + # http session + http_session = http_session_with_retry() + http_session.verify = ca_cert.as_posix() + + # no rune provided => no websocket connection and no notification received + notifications = notifications_received_via_websocket(l1, base_url, http_session) + assert len(notifications) == 0 + + +def test_clnrest_websocket_wrong_rune(node_factory): + """Test websocket with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + + # http session + http_session = http_session_with_retry() + http_session.verify = ca_cert.as_posix() + + # wrong rune provided => no websocket connection and no notification received + http_session.headers.update({"rune": "jMHrjVJb5l9-mjEd7zwux7Ookra1fgZ8wa9D8QbVT-w9MA=="}) + + notifications = notifications_received_via_websocket(l1, base_url, http_session) + l1.daemon.logsearch_start = 0 + assert l1.daemon.is_in_log(r"error: {'code': 1501, 'message': 'Not authorized: Not derived from master'}") + assert len(notifications) == 0 + + +def test_clnrest_websocket_unrestricted_rune(node_factory): + """Test websocket with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + + # http session + http_session = http_session_with_retry() + http_session.verify = ca_cert.as_posix() + + # unrestricted rune provided => websocket connection and notifications received + rune_unrestricted = l1.rpc.createrune()['rune'] + http_session.headers.update({"rune": rune_unrestricted}) + notifications = notifications_received_via_websocket(l1, base_url, http_session) + assert len([n for n in notifications if not n.get('invoice_creation') is None]) == 1 + + +def test_clnrest_websocket_rune_readonly(node_factory): + """Test websocket with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + + # http session + http_session = http_session_with_retry() + http_session.verify = ca_cert.as_posix() + + # readonly rune provided => websocket connection and notifications received + rune_readonly = l1.rpc.createrune(restrictions="readonly")['rune'] + http_session.headers.update({"rune": rune_readonly}) + notifications = notifications_received_via_websocket(l1, base_url, http_session) + assert len([n for n in notifications if not n.get('invoice_creation') is None]) == 1 + + +def test_clnrest_websocket_rune_listnotifications(node_factory): + """Test websocket with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + + # http session + http_session = http_session_with_retry() + http_session.verify = ca_cert.as_posix() + + # rune authorizing listclnrest-notifications method provided => websocket connection and notifications received + rune_clnrest_notifications = l1.rpc.createrune(restrictions=[["method=listclnrest-notifications"]])['rune'] + http_session.headers.update({"rune": rune_clnrest_notifications}) + notifications = notifications_received_via_websocket(l1, base_url, http_session) + assert len([n for n in notifications if not n.get('invoice_creation') is None]) == 1 + + +def test_clnrest_websocket_rune_no_listnotifications(node_factory): + """Test websocket with default values for options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + + # http session + http_session = http_session_with_retry() + http_session.verify = ca_cert.as_posix() + + # with a rune which doesn't authorized listclnrest-notifications method => no websocket connection and no notification received + rune_no_clnrest_notifications = l1.rpc.createrune(restrictions=[["method/listclnrest-notifications"]])['rune'] + http_session.headers.update({"rune": rune_no_clnrest_notifications}) + notifications = notifications_received_via_websocket(l1, base_url, http_session) + assert len([n for n in notifications if n.find('invoice_creation') > 0]) == 0 + + +def test_clnrest_options(node_factory): + """Test startup options `clnrest-host`, `clnrest-protocol` and `clnrest-certs`.""" + # with invalid port + rest_port = 1000 + l1 = node_factory.get_node(options={'clnrest-port': rest_port}) + assert l1.daemon.is_in_log(f'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-port` {rest_port}, should be a valid available port between 1024 and 65535.') + + # with invalid protocol + rest_port = str(reserve()) + rest_protocol = 'htttps' + l1 = node_factory.get_node(options={'clnrest-port': rest_port, + 'clnrest-protocol': rest_protocol}) + assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-protocol` can either be http or https.') + + # with invalid host + rest_port = str(reserve()) + rest_host = '127.0.0.12.15' + l1 = node_factory.get_node(options={'clnrest-port': rest_port, + 'clnrest-host': rest_host}) + assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-host` should be a valid IP.') + + +def test_clnrest_http_headers(node_factory): + """Test HTTP headers set with `clnrest-csp` and `clnrest-cors-origins` options.""" + # start a node with clnrest + l1, base_url, ca_cert = start_node_with_clnrest(node_factory) + http_session = http_session_with_retry() + + # Default values for `clnrest-csp` and `clnrest-cors-origins` options + response = http_session.get(base_url + '/v1/list-methods', verify=ca_cert) + assert response.headers['Content-Security-Policy'] == "default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';" + assert response.headers['Access-Control-Allow-Origin'] == '*' + # This might happen really early! + l1.daemon.logsearch_start = 0 + l1.daemon.wait_for_log(f'plugin-clnrest.py: REST server running at {base_url}') + + # Custom values for `clnrest-csp` and `clnrest-cors-origins` options + rest_port = str(reserve()) + rest_certs = node_factory.directory + '/clnrest-certs' + l2 = node_factory.get_node(options={ + 'clnrest-port': rest_port, + 'clnrest-certs': rest_certs, + 'clnrest-csp': "default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';", + 'clnrest-cors-origins': ['https://localhost:5500', 'http://192.168.1.30:3030', 'http://192.168.1.10:1010'] + }) + base_url = 'https://127.0.0.1:' + rest_port + # This might happen really early! + l2.daemon.logsearch_start = 0 + l2.daemon.wait_for_log(f'plugin-clnrest.py: REST server running at {base_url}') + ca_cert = Path(rest_certs) / 'ca.pem' + + response = http_session.get(base_url + '/v1/list-methods', + headers={'Origin': 'http://192.168.1.30:3030'}, + verify=ca_cert) + assert response.headers['Content-Security-Policy'] == "default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';" + assert response.headers['Access-Control-Allow-Origin'] == 'http://192.168.1.30:3030' + response = http_session.get(base_url + '/v1/list-methods', + headers={'Origin': 'http://192.168.1.10:1010'}, + verify=ca_cert) + assert response.headers['Access-Control-Allow-Origin'] == 'http://192.168.1.10:1010' diff --git a/tests/test_closing.py b/tests/test_closing.py index 28e40fac0944..727238d0eea3 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -482,6 +482,7 @@ def test_closing_negotiation_step_700sat(node_factory, bitcoind, chainparams): closing_negotiation_step(node_factory, bitcoind, chainparams, opts) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "dev_sign_last_tx causes subsequent validate_holder_commitment_tx failure") @pytest.mark.parametrize("anchors", [False, True]) def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams, anchors): """Test penalty transaction with an incoming HTLC""" @@ -614,6 +615,7 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams, anchors): check_utxos_channel(l2, [channel_id], expected_2, tags) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "dev_sign_last_tx causes subsequent validate_holder_commitment_tx failure") @pytest.mark.parametrize("anchors", [False, True]) def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams, anchors): """Test penalty transaction with an outgoing HTLC""" @@ -750,6 +752,7 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams, anchors) @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "handle_sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') @pytest.mark.slow_test def test_channel_lease_falls_behind(node_factory, bitcoind): @@ -791,6 +794,7 @@ def test_channel_lease_falls_behind(node_factory, bitcoind): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "handle_sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') @pytest.mark.slow_test def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): @@ -892,6 +896,7 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "handle_sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') @pytest.mark.slow_test def test_channel_lease_unilat_closes(node_factory, bitcoind): @@ -1004,6 +1009,7 @@ def test_channel_lease_unilat_closes(node_factory, bitcoind): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "handle_sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") def test_channel_lease_lessor_cheat(node_factory, bitcoind, chainparams): @@ -1080,6 +1086,7 @@ def test_channel_lease_lessor_cheat(node_factory, bitcoind, chainparams): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "handle_sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") def test_channel_lease_lessee_cheat(node_factory, bitcoind, chainparams): @@ -1156,6 +1163,7 @@ def test_channel_lease_lessee_cheat(node_factory, bitcoind, chainparams): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "policy: can't sign revoked commitment number") # FIXME - should work w/ VLS_PERMISSIVE @pytest.mark.slow_test @pytest.mark.parametrize("anchors", [False, True]) def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams, anchors): @@ -1340,6 +1348,7 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams, anchors): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "policy: can't sign revoked commitment number") # FIXME - should work with VLS_PERMISSIVE @pytest.mark.slow_test @pytest.mark.parametrize("anchors", [False, True]) def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams, anchors): @@ -1568,6 +1577,7 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams, anchors): assert acc['resolved_at_block'] > 0 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "exceeds max fee policy") @pytest.mark.parametrize("anchors", [False, True]) def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams, anchors): ''' @@ -1811,6 +1821,7 @@ def test_onchaind_replay(node_factory, bitcoind): 'delay': 101, 'channel': first_scid(l1, l2) } + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) l1.daemon.wait_for_log('sendrawtx exit 0') bitcoind.generate_block(1, wait_for_mempool=1) @@ -1954,6 +1965,7 @@ def test_onchain_timeout(node_factory, bitcoind, executor, chainparams, anchors) 'channel': first_scid(l1, l2) } + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret'], groupid=1) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) @@ -2088,6 +2100,7 @@ def test_onchain_middleman_simple(node_factory, bitcoind, chainparams, anchors): q = queue.Queue() + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming def try_pay(): try: l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) @@ -2227,6 +2240,7 @@ def test_onchain_middleman_their_unilateral_in(node_factory, bitcoind, chainpara q = queue.Queue() + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming def try_pay(): try: l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) @@ -2339,6 +2353,8 @@ def try_pay(): try: # rhash is fake (so is payment_secret) rhash = 'B1' * 32 + # let the signer know this payment is coming + l1.rpc.preapprovekeysend(l2.info['id'], rhash, 10**8) l1.rpc.sendpay(route, rhash, payment_secret=rhash) q.put(None) except Exception as err: @@ -2479,6 +2495,7 @@ def test_onchain_feechange(node_factory, bitcoind, executor): 'channel': first_scid(l1, l2) } + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming executor.submit(l1.rpc.sendpay, [routestep], rhash, payment_secret=inv['payment_secret']) # l2 will drop to chain. @@ -2559,6 +2576,7 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): 'channel': first_scid(l1, l2) } + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming executor.submit(l1.rpc.sendpay, [routestep], rhash, payment_secret=inv['payment_secret']) # l2 will drop to chain. @@ -2851,6 +2869,7 @@ def route_to_l1(src): @pytest.mark.slow_test +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "frequently flakes") def test_onchain_multihtlc_our_unilateral(node_factory, bitcoind): """Node pushes a channel onchain with multiple HTLCs with same payment_hash """ h, l1, l2, l3, l4, l5, l6, l7 = setup_multihtlc_test(node_factory, bitcoind) @@ -2906,6 +2925,7 @@ def test_onchain_multihtlc_our_unilateral(node_factory, bitcoind): @pytest.mark.slow_test +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "frequently flakes") def test_onchain_multihtlc_their_unilateral(node_factory, bitcoind): """Node pushes a channel onchain with multiple HTLCs with same payment_hash """ h, l1, l2, l3, l4, l5, l6, l7 = setup_multihtlc_test(node_factory, bitcoind) @@ -3068,7 +3088,7 @@ def test_permfail_htlc_out(node_factory, bitcoind, executor): bitcoind.generate_block(2) wait_for(lambda: l2.rpc.listpeers()['peers'] == []) - +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "policy: can't withdraw to non-wallet address") # FIXME - should work with auto-approve def test_permfail(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2) @@ -3168,6 +3188,7 @@ def test_shutdown(node_factory): l1.rpc.stop() +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "policy failure: validate_mutual_close_tx: holder_script doesn't match upfront holder_shutdown_script") # FIXME - should work with VLS_PERMISSIVE def test_option_upfront_shutdown_script(node_factory, bitcoind, executor, chainparams): l1 = node_factory.get_node(start=False, allow_warning=True) # Insist on upfront script we're not going to match. @@ -3740,6 +3761,7 @@ def test_closing_anchorspend_htlc_tx_rbf(node_factory, bitcoind): 'delay': 12, 'channel': first_scid(l1, l2) } + l1.rpc.preapprovekeysend(routestep['id'], rhash, routestep['amount_msat']) l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) l2.daemon.wait_for_log('dev_disconnect') l2.stop() @@ -3807,6 +3829,7 @@ def test_htlc_no_force_close(node_factory, bitcoind, anchors): 'id': l3.info['id'], 'delay': 10, 'channel': first_scid(l2, l3)}] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) l3.daemon.wait_for_log('dev_disconnect') @@ -3928,6 +3951,7 @@ def test_peer_anchor_push(node_factory, bitcoind, executor, chainparams): amt = 100_000_000 sticky_inv = l3.rpc.invoice(amt, 'sticky', 'sticky') route = l1.rpc.getroute(l3.info['id'], amt, 1)['route'] + l1.rpc.preapproveinvoice(bolt11=sticky_inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, sticky_inv['payment_hash'], payment_secret=sticky_inv['payment_secret']) l3.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') diff --git a/tests/test_connection.py b/tests/test_connection.py index 570faaa54bbc..9c0b5009d233 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -584,6 +584,7 @@ def test_disconnect_fundee(node_factory): assert len(l2.rpc.listpeers()) == 1 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_disconnect_fundee_v2(node_factory): @@ -660,6 +661,7 @@ def test_disconnect_half_signed_v2(node_factory): assert len(l1.rpc.listpeerchannels(l2.info['id'])['channels']) == 1 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_reconnect_signed(node_factory): @@ -844,6 +846,7 @@ def test_reconnect_sender_add1(node_factory): l1.daemon.wait_for_log('Already have funding locked in') # This will send commit, so will reconnect as required. + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) @@ -874,6 +877,7 @@ def test_reconnect_sender_add(node_factory): route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}] # This will send commit, so will reconnect as required. + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) # Should have printed this for every reconnect. for i in range(0, len(disconnects)): @@ -905,6 +909,7 @@ def test_reconnect_receiver_add(node_factory): assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') @@ -934,6 +939,7 @@ def test_reconnect_receiver_fulfill(node_factory): assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') @@ -1209,6 +1215,7 @@ def test_v2_open(node_factory, bitcoind, chainparams): assert(result['status'] == 'complete') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "channel push not allowed: dual-funding not supported yet") @pytest.mark.openchannel('v1') def test_funding_push(node_factory, bitcoind, chainparams): """ Try to push peer some sats """ @@ -1639,6 +1646,7 @@ def test_funding_v2_cancel_race(node_factory, bitcoind, executor): @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "remote_hsmd can't handle random external addresses (allowlist)") # FIXME - should work w/ auto-approve def test_funding_close_upfront(node_factory, bitcoind): opts = {'plugin': os.path.join(os.getcwd(), 'tests/plugins/openchannel_hook_accepter.py')} @@ -1786,6 +1794,7 @@ def test_funding_external_wallet(node_factory, bitcoind): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v1') # We manually turn on dual-funding for select nodes +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "commit c0cc285a causes: channel stub can only return point for commitment number zero") def test_multifunding_v1_v2_mixed(node_factory, bitcoind): ''' Simple test for multifundchannel, using v1 + v2 @@ -1832,6 +1841,7 @@ def test_multifunding_v1_v2_mixed(node_factory, bitcoind): l1.rpc.pay(inv) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_multifunding_v2_exclusive(node_factory, bitcoind): @@ -2047,6 +2057,7 @@ def test_multifunding_wumbo(node_factory): l1.rpc.multifundchannel(destinations) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "flakes too frequently w/ VLS") @unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Fees on elements are different") @pytest.mark.openchannel('v1') # v2 the weight calculation is off by 3 @pytest.mark.parametrize("anchors", [False, True]) @@ -2440,6 +2451,7 @@ def test_update_fee(node_factory, bitcoind): l2.daemon.wait_for_log('onchaind complete, forgetting peer') +@pytest.mark.developer def test_fee_limits(node_factory, bitcoind): l1, l2, l3, l4 = node_factory.get_nodes(4, opts=[{'dev-max-fee-multiplier': 5, 'may_reconnect': True, 'allow_warning': True}, @@ -3573,6 +3585,8 @@ def test_channel_features(node_factory, bitcoind, anchors): assert only_one(l2.rpc.listpeerchannels()['channels'])['features'] == chan['features'] +@pytest.mark.developer("need dev-force-features") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support non-option_static_remotekey") # FIXME - should work with VLS_PERMISSIVE def test_nonstatic_channel(node_factory, bitcoind): """Smoke test for a channel without option_static_remotekey""" l1, l2 = node_factory.line_graph(2, @@ -3688,6 +3702,7 @@ def test_openchannel_init_alternate(node_factory, executor): print("nothing to do") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "upgrade not yet supported by VLS") def test_upgrade_statickey(node_factory, executor): """l1 doesn't have option_static_remotekey, l2 offers it.""" l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, @@ -3718,6 +3733,7 @@ def test_upgrade_statickey(node_factory, executor): l2.daemon.wait_for_log(r"They sent desired_channel_type \[12\]") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "upgrade not yet supported by VLS") def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): """We test penalty before/after, and unilateral before/after""" l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, @@ -3852,6 +3868,7 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): wait_for(lambda: len(l2.rpc.listpeerchannels()['channels']) == 0) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "upgrade not yet supported by VLS") def test_upgrade_statickey_fail(node_factory, executor, bitcoind): """We reconnect at all points during retransmit, and we won't upgrade.""" l1_disconnects = ['-WIRE_COMMITMENT_SIGNED', @@ -4165,6 +4182,7 @@ def test_multichan(node_factory, executor, bitcoind): before = l2.rpc.listpeerchannels(l3.info['id'])['channels'] inv1 = l3.rpc.invoice(100000000, "invoice", "invoice") + l1.rpc.preapproveinvoice(bolt11=inv1['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv1['payment_hash'], payment_secret=inv1['payment_secret']) l1.rpc.waitsendpay(inv1['payment_hash']) @@ -4192,6 +4210,7 @@ def test_multichan(node_factory, executor, bitcoind): before = l2.rpc.listpeerchannels(l3.info['id'])['channels'] route[1]['channel'] = scid23b inv2 = l3.rpc.invoice(100000000, "invoice2", "invoice2") + l1.rpc.preapproveinvoice(bolt11=inv2['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv2['payment_hash'], payment_secret=inv2['payment_secret']) l1.rpc.waitsendpay(inv2['payment_hash']) # Wait until HTLCs fully settled @@ -4236,6 +4255,7 @@ def test_multichan(node_factory, executor, bitcoind): # We can actually pay by *closed* scid (at least until it's completely forgotten) route[1]['channel'] = scid23a inv3 = l3.rpc.invoice(100000000, "invoice3", "invoice3") + l1.rpc.preapproveinvoice(bolt11=inv3['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv3['payment_hash'], payment_secret=inv3['payment_secret']) l1.rpc.waitsendpay(inv3['payment_hash']) diff --git a/tests/test_db.py b/tests/test_db.py index 82b3e3b5c930..aa881cd18a6e 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -136,6 +136,7 @@ def test_max_channel_id(node_factory, bitcoind): @unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete db") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot") @unittest.skipIf(TEST_NETWORK != 'regtest', "The network must match the DB snapshot") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't like channel_nonce changing") def test_scid_upgrade(node_factory, bitcoind): bitcoind.generate_block(1) @@ -167,6 +168,7 @@ def test_scid_upgrade(node_factory, bitcoind): @unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete db") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot") @unittest.skipIf(TEST_NETWORK != 'regtest', "The network must match the DB snapshot") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't like channel_nonce changing") def test_last_tx_inflight_psbt_upgrade(node_factory, bitcoind): bitcoind.generate_block(12) @@ -185,6 +187,7 @@ def test_last_tx_inflight_psbt_upgrade(node_factory, bitcoind): @unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete db") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot") @unittest.skipIf(TEST_NETWORK != 'regtest', "The network must match the DB snapshot") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't like channel_nonce changing") def test_last_tx_psbt_upgrade(node_factory, bitcoind): bitcoind.generate_block(12) @@ -219,6 +222,7 @@ def test_last_tx_psbt_upgrade(node_factory, bitcoind): @pytest.mark.slow_test @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot") @unittest.skipIf(TEST_NETWORK != 'regtest', "The network must match the DB snapshot") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support backfill yet") def test_backfill_scriptpubkeys(node_factory, bitcoind): bitcoind.generate_block(214) @@ -352,6 +356,7 @@ def get_dsn(self): os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot" ) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support DB migration") def test_local_basepoints_cache(bitcoind, node_factory): """XXX started caching the local basepoints as well as the remote ones. @@ -440,6 +445,7 @@ def test_sqlite3_builtin_backup(bitcoind, node_factory): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Don't know how to swap dbs in Postgres") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "vlsd chokes on allowlist when started on wrong network") def test_db_sanity_checks(bitcoind, node_factory): l1, l2 = node_factory.get_nodes(2, opts=[{'allow_broken_log': True, 'may_fail': True}, {}]) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index cf8815cc00a8..8714c8e50731 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -182,6 +182,7 @@ def test_announce_dns_suppressed(node_factory, bitcoind): assert addresses[0]['port'] == 1236 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "trouble with IPv6 in VLS CI runner") def test_announce_and_connect_via_dns(node_factory, bitcoind): """ Test that DNS annoucements propagate and can be used when connecting. diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 5d72725eea34..e4c2746dfe13 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -130,6 +130,7 @@ def test_invoice_weirdstring(node_factory): l1.rpc.delinvoice(weird_label, "unpaid") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "VLS policies catch two-invoice-one-preimage ahead of the node") # FIXME - should work with VLS_PERMISSIVE def test_invoice_preimage(node_factory): """Test explicit invoice 'preimage'. """ diff --git a/tests/test_misc.py b/tests/test_misc.py index 9bea5e75e3fd..d6f5734bc358 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1145,6 +1145,7 @@ def test_cli_commando(node_factory): assert only_one(j['invoices'])['label'] == 'l"[]{}' +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd integration test job control fails here") def test_daemon_option(node_factory): """ Make sure --daemon at least vaguely works! @@ -2173,6 +2174,7 @@ def test_bitcoind_feerate_floor(node_factory, bitcoind, anchors): @unittest.skipIf(TEST_NETWORK != 'regtest', "Addresses are network specific") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support forced secrets") def test_dev_force_bip32_seed(node_factory): l1 = node_factory.get_node(options={'dev-force-bip32-seed': '0000000000000000000000000000000000000000000000000000000000000001'}) # First is m/0/0/1 .. @@ -2511,6 +2513,7 @@ def test_regtest_upgrade(node_factory): @unittest.skipIf(VALGRIND, "valgrind files can't be written since we rmdir") @unittest.skipIf(TEST_NETWORK != "regtest", "needs bitcoin mainnet") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't create hsm_secret file") def test_new_node_is_mainnet(node_factory): """Test that an empty directory causes us to be on mainnet""" l1 = node_factory.get_node(start=False, may_fail=True) @@ -2549,13 +2552,13 @@ def test_unicode_rpc(node_factory, executor, bitcoind): @unittest.skipIf(VALGRIND, "Testing pyln doesn't exercise anything interesting in the c code.") -def test_unix_socket_path_length(node_factory, bitcoind, directory, executor, db_provider, test_base_dir): +def test_unix_socket_path_length(node_factory, bitcoind, lssd, directory, executor, db_provider, test_base_dir): lightning_dir = os.path.join(directory, "anode" + "far" * 30 + "away") os.makedirs(lightning_dir) db = db_provider.get_db(lightning_dir, "test_unix_socket_path_length", 1) db.provider = db_provider - l1 = LightningNode(1, lightning_dir, bitcoind, executor, VALGRIND, db=db, port=reserve()) + l1 = LightningNode(1, lightning_dir, bitcoind, lssd, executor, VALGRIND, db=db, port=reserve()) # `LightningNode.start()` internally calls `LightningRpc.getinfo()` which # exercises the socket logic, and raises an issue if it fails. @@ -2674,6 +2677,7 @@ def test_sendcustommsg(node_factory): ]) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support dev-force-privkey") def test_makesecret(node_factory): """ Test makesecret command. @@ -2711,6 +2715,7 @@ def test_staticbackup(node_factory): and l1.rpc.staticbackup()["scb"][0][16: 16 + 64] == _["channel_id"]) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd says no such channel") def test_recoverchannel(node_factory): """ Test recoverchannel @@ -3744,6 +3749,7 @@ def test_setconfig(node_factory, bitcoind): assert len(lines) == 3 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support this command") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "deletes database, which is assumed sqlite3") def test_recover_command(node_factory, bitcoind): l1, l2 = node_factory.get_nodes(2) diff --git a/tests/test_opening.py b/tests/test_opening.py index 25ff00fd1eb7..b7d2badf0693 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -7,6 +7,7 @@ from pathlib import Path from pprint import pprint +import os import pytest import re import unittest @@ -18,6 +19,7 @@ def find_next_feerate(node, peer): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "handle_sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') def test_queryrates(node_factory, bitcoind): @@ -57,6 +59,7 @@ def test_queryrates(node_factory, bitcoind): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v1') # Mixed v1 + v2, v2 manually turned on +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "commit c0cc285a causes: channel stub can only return point for commitment number zero") def test_multifunding_v2_best_effort(node_factory, bitcoind): ''' Check that best_effort flag works. @@ -172,6 +175,7 @@ def test_v2_open_sigs_reconnect_2(node_factory, bitcoind): l2.daemon.wait_for_log(r'to CHANNELD_NORMAL') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_v2_open_sigs_reconnect_1(node_factory, bitcoind): @@ -334,6 +338,7 @@ def test_v2_open_sigs_restart_while_dead(node_factory, bitcoind): l1.daemon.wait_for_log(r'to CHANNELD_NORMAL') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_v2_rbf_single(node_factory, bitcoind, chainparams): @@ -434,6 +439,7 @@ def test_v2_rbf_single(node_factory, bitcoind, chainparams): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "handle_sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') def test_v2_rbf_abort_retry(node_factory, bitcoind, chainparams): l1, l2 = node_factory.get_nodes(2, opts={'allow_warning': True}) @@ -515,6 +521,7 @@ def test_v2_rbf_abort_retry(node_factory, bitcoind, chainparams): assert not l2.daemon.is_in_log('WIRE_CHANNEL_REESTABLISH') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_v2_rbf_abort_channel_opens(node_factory, bitcoind, chainparams): @@ -574,6 +581,7 @@ def test_v2_rbf_abort_channel_opens(node_factory, bitcoind, chainparams): l1.daemon.wait_for_log(' to CHANNELD_NORMAL') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "sign_option_will_fund_offer unimplemented") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): @@ -670,6 +678,7 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): l1.daemon.wait_for_log('State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_v2_rbf_multi(node_factory, bitcoind, chainparams): @@ -860,6 +869,7 @@ def test_rbf_reconnect_ack(node_factory, bitcoind, chainparams): l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): @@ -981,6 +991,7 @@ def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): l2.daemon.wait_for_log(r'to CHANNELD_NORMAL') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_rbf_reconnect_tx_sigs(node_factory, bitcoind, chainparams): @@ -1062,6 +1073,7 @@ def test_rbf_reconnect_tx_sigs(node_factory, bitcoind, chainparams): assert l1_funding_txid == l2_funding_txid +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_rbf_to_chain_before_commit(node_factory, bitcoind, chainparams): @@ -1163,6 +1175,7 @@ def test_rbf_no_overlap(node_factory, bitcoind, chainparams): l1.rpc.openchannel_update(chan_id, bump['psbt']) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_rbf_fails_to_broadcast(node_factory, bitcoind, chainparams): @@ -1244,6 +1257,7 @@ def run_retry(): assert last_txs['tx'] +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_rbf_broadcast_close_inflights(node_factory, bitcoind, chainparams): @@ -1319,6 +1333,7 @@ def run_retry(): assert inflights[1]['scratch_txid'] not in bitcoind.rpc.getrawmempool() +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_rbf_non_last_mined(node_factory, bitcoind, chainparams): @@ -1419,6 +1434,7 @@ def censoring_sendrawtx(r): l1.daemon.wait_for_log(r'to ONCHAIN') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_funder_options(node_factory, bitcoind): @@ -1486,6 +1502,7 @@ def test_funder_options(node_factory, bitcoind): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "dual-funding not supported yet") # FIXME - should work with VLS_PERMISSIVE def test_funder_contribution_limits(node_factory, bitcoind): opts = {'experimental-dual-fund': None, 'feerates': (5000, 5000, 5000, 5000)} @@ -1547,6 +1564,7 @@ def test_funder_contribution_limits(node_factory, bitcoind): assert l3.daemon.is_in_log(r'calling `signpsbt` .* 6 inputs') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') def test_inflight_dbload(node_factory, bitcoind): """Bad db field access breaks Postgresql on startup with opening leases""" @@ -1625,6 +1643,7 @@ def test_zeroconf_mindepth(bitcoind, node_factory): wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == "CHANNELD_NORMAL") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "tried commitment when funding is not buried ") def test_zeroconf_open(bitcoind, node_factory): """Let's open a zeroconf channel @@ -1689,6 +1708,7 @@ def test_zeroconf_open(bitcoind, node_factory): l2.rpc.pay(inv) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd inplace and socket don't generate WIRE_HSMD_CUPDATE_SIG_REQ log messages") def test_zeroconf_public(bitcoind, node_factory, chainparams): """Test that we transition correctly from zeroconf to public @@ -1792,6 +1812,7 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): wait_for(lambda: only_one([x for x in n.rpc.bkpr_listbalances()['accounts'] if x['account'] == channel_id])['account_resolved']) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "tried commitment when funding is not buried ") def test_zeroconf_forward(node_factory, bitcoind): """Ensure that we can use zeroconf channels in forwards. @@ -1856,6 +1877,7 @@ def test_buy_liquidity_ad_no_v2(node_factory, bitcoind): compact_lease='029a002d000000004b2003e8') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') def test_v2_replay_bookkeeping(node_factory, bitcoind): """ Test that your bookkeeping for a liquidity ad is good @@ -1920,6 +1942,7 @@ def test_v2_replay_bookkeeping(node_factory, bitcoind): l1.rpc.bkpr_listbalances() +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "sign_option_will_fund_offer unimplemented") @pytest.mark.openchannel('v2') def test_buy_liquidity_ad_check_bookkeeping(node_factory, bitcoind): """ Test that your bookkeeping for a liquidity ad is good.""" @@ -2040,6 +2063,7 @@ def test_scid_alias_private(node_factory, bitcoind): l1.rpc.waitsendpay(inv['payment_hash']) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "tried commitment when funding is not buried ") def test_zeroconf_multichan_forward(node_factory): """The freedom to choose the forward channel bytes us when it is 0conf @@ -2325,6 +2349,7 @@ def _no_utxo_response(r): wait_for(lambda: l2.rpc.listpeerchannels()['channels'] == []) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v2') def test_openchannel_no_unconfirmed_inputs_accepter(node_factory, bitcoind): """ If the accepter flags 'require-confirmed-inputs' for an open, diff --git a/tests/test_pay.py b/tests/test_pay.py index 919ac39c5836..33f8275097e6 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -552,6 +552,7 @@ def test_pay_maxfee_shadow(node_factory): assert pay_status["amount_msat"] == Millisatoshi(amount) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "fails multiple policy checks") def test_sendpay(node_factory): l1, l2 = node_factory.line_graph(2, fundamount=10**6) @@ -574,6 +575,7 @@ def invoice_unpaid(dst, label): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['amount_msat'] = rs['amount_msat'] - 1 + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -582,6 +584,7 @@ def invoice_unpaid(dst, label): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['amount_msat'] = rs['amount_msat'] * 2 + 1 + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -590,6 +593,7 @@ def invoice_unpaid(dst, label): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['delay'] = rs['delay'] - 2 + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -599,17 +603,20 @@ def invoice_unpaid(dst, label): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['id'] = '00000000000000000000000000000000' + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) assert invoice_unpaid(l2, 'testpayment2') l1.rpc.check_request_schemas = True # Bad payment_secret + l1.rpc.preapprovekeysend(l2.info['id'], rhash, routestep['amount_msat']) l1.rpc.sendpay([routestep], rhash, payment_secret="00" * 32) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') # Missing payment_secret + l1.rpc.preapprovekeysend(l2.info['id'], rhash, routestep['amount_msat']) l1.rpc.sendpay([routestep], rhash) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) @@ -625,6 +632,7 @@ def invoice_unpaid(dst, label): # This works. before = int(time.time()) + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming details = l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) after = int(time.time()) preimage = l1.rpc.waitsendpay(rhash)['payment_preimage'] @@ -667,6 +675,7 @@ def check_balances(): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'unpaid' routestep = {'amount_msat': amt * 2, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)} + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) preimage3 = l1.rpc.waitsendpay(rhash)['payment_preimage'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'paid' @@ -702,6 +711,7 @@ def test_repay(node_factory): 'delay': 5, 'channel': first_scid(l1, l2) } + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) l1.daemon.wait_for_log("Sending 200000000msat over 1 hops to deliver 200000000msat") l1.rpc.waitsendpay(inv['payment_hash'])['payment_preimage'] @@ -730,6 +740,7 @@ def test_wait_sendpay(node_factory, executor): 'delay': 5, 'channel': first_scid(l1, l2) } + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) assert wait_created.result(TIMEOUT) == {'subsystem': 'sendpays', 'created': 1, @@ -1157,6 +1168,7 @@ def test_forward(node_factory, bitcoind): # Unknown other peer route = copy.deepcopy(baseroute) route[1]['id'] = '031a8dc444e41bb989653a4501e11175a488a57439b0c4947704fd6e3de5dca607' + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) @@ -1559,6 +1571,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): l2.rpc.close(c23, 1) + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming with pytest.raises(RpcError): l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) @@ -1584,6 +1597,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): 'delay': 6, 'channel': c24}] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming with pytest.raises(RpcError): l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) @@ -1610,6 +1624,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): 'delay': 6, 'channel': c25}] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming with pytest.raises(RpcError): l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) @@ -1633,6 +1648,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): # Replace id with a different pubkey, so onion encoded badly at l2 hop. route[1]['id'] = mangled_nodeid + l6.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming with pytest.raises(RpcError): l6.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l6.rpc.waitsendpay(payment_hash) @@ -1660,6 +1676,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): 'delay': 5, 'channel': c24}] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming executor.submit(l1.rpc.sendpay, route, payment_hash, payment_secret=inv['payment_secret']) l4.daemon.wait_for_log('permfail') @@ -1715,6 +1732,7 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): # L2 tries to pay r = l2.rpc.getroute(l4.info['id'], 10**8, 1)["route"] + l2.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l2.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Now increment CLTV @@ -1723,6 +1741,7 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): # L1 tries to pay r = l1.rpc.getroute(l4.info['id'], 10**8, 1)["route"] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Now increment CLTV @@ -1731,6 +1750,7 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): # L3 tries to pay r = l3.rpc.getroute(l4.info['id'], 10**8, 1)["route"] + l3.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l3.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Give them time to go through. @@ -1796,6 +1816,7 @@ def exhaust_channel(opener, peer, scid, already_spent=0): 'delay': 10, 'channel': scid } + opener.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming opener.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) opener.rpc.waitsendpay(inv['payment_hash']) @@ -2493,6 +2514,7 @@ def test_setchannel_startup_opts(node_factory, bitcoind): assert result[1]['htlc_maximum_msat'] == Millisatoshi(5) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "invoice with any amount") def test_channel_spendable(node_factory, bitcoind): """Test that spendable_msat is accurate""" sats = 10**6 @@ -2513,6 +2535,7 @@ def test_channel_spendable(node_factory, bitcoind): # Exact amount should succeed. route = l1.rpc.getroute(l2.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # Amount should drop to 0 once HTLC is sent; we have time, thanks to @@ -2538,6 +2561,7 @@ def test_channel_spendable(node_factory, bitcoind): # Exact amount should succeed. route = l2.rpc.getroute(l1.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] + l2.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l2.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # Amount should drop to 0 once HTLC is sent; we have time, thanks to @@ -2547,6 +2571,7 @@ def test_channel_spendable(node_factory, bitcoind): l2.rpc.waitsendpay(payment_hash, TIMEOUT) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "invoice with any amount") def test_channel_receivable(node_factory, bitcoind): """Test that receivable_msat is accurate""" sats = 10**6 @@ -2602,6 +2627,7 @@ def test_channel_receivable(node_factory, bitcoind): l2.rpc.waitsendpay(payment_hash, TIMEOUT) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "invoice with any amount") def test_channel_spendable_large(node_factory, bitcoind): """Test that spendable_msat is accurate for large channels""" # This is almost the max allowable spend. @@ -2697,6 +2723,7 @@ def test_htlc_too_dusty_outgoing(node_factory, bitcoind, chainparams): route = l1.rpc.getroute(l2.info['id'], non_dust_htlc_val_sat * 1000, 1)['route'] for i in range(0, 3): inv = l2.rpc.invoice((non_dust_htlc_val_sat * 1000), str(i + 100), str(i + 100)) + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) l2.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs') res = only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments']) @@ -2706,6 +2733,7 @@ def test_htlc_too_dusty_outgoing(node_factory, bitcoind, chainparams): route = l1.rpc.getroute(l2.info['id'], htlc_val_msat, 1)['route'] for i in range(0, num_dusty_htlcs): inv = l2.rpc.invoice(htlc_val_msat, str(i), str(i)) + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) l2.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs') res = only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments']) @@ -2713,6 +2741,7 @@ def test_htlc_too_dusty_outgoing(node_factory, bitcoind, chainparams): # one more should tip it over, and return a payment failure inv = l2.rpc.invoice(htlc_val_msat, str(num_dusty_htlcs), str(num_dusty_htlcs)) + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) l1.daemon.wait_for_log('CHANNEL_ERR_DUST_FAILURE') wait_for(lambda: only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments'])['status'] == 'failed') @@ -2720,6 +2749,7 @@ def test_htlc_too_dusty_outgoing(node_factory, bitcoind, chainparams): # but we can still add a non dust htlc route = l1.rpc.getroute(l2.info['id'], non_dust_htlc_val_sat * 1000, 1)['route'] inv = l2.rpc.invoice((10000 * 1000), str(120), str(120)) + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) l2.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs') res = only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments']) @@ -2740,6 +2770,7 @@ def test_htlc_too_dusty_outgoing(node_factory, bitcoind, chainparams): l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "feerate above maximum (escalates)") def test_htlc_too_dusty_incoming(node_factory, bitcoind): """ Try to hit the 'too much dust' limit, should fail the HTLC """ feerate = 30000 @@ -2802,6 +2833,7 @@ def test_error_returns_blockheight(node_factory, bitcoind): == '400f{:016x}{:08x}'.format(100, bitcoind.rpc.getblockcount())) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "inv_nosecret: The invoice is missing the mandatory payment secret") @unittest.skipIf(TEST_NETWORK != 'regtest', "Invoice is network specific") def test_pay_no_secret(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, wait_for_announce=True) @@ -4179,6 +4211,7 @@ def test_mpp_interference_2(node_factory, bitcoind, executor): @pytest.mark.slow_test +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "remote_hsmd doesn't allow push of greater than 20k sat") def test_mpp_overload_payee(node_factory, bitcoind): """ We had a bug where if the payer is unusually well-connected compared @@ -4413,6 +4446,7 @@ def test_offer(node_factory, bitcoind): assert 'recurrence: every 600 seconds paywindow -10 to +600 (pay proportional)\n' in output +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "Invalid bech32: invalid checksum") def test_offer_deprecated_api(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, opts={'experimental-offers': None, 'allow-deprecated-apis': True}) @@ -4438,6 +4472,7 @@ def test_fetchinvoice_3hop(node_factory, bitcoind): l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "invoice from offer: Invalid bech32: invalid checksum") def test_fetchinvoice(node_factory, bitcoind): # We remove the conversion plugin on l3, causing it to get upset. l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, @@ -4563,6 +4598,7 @@ def test_fetchinvoice(node_factory, bitcoind): l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'timeout': 10}) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "Invalid bech32: invalid checksum") def test_fetchinvoice_recurrence(node_factory, bitcoind): """Test for our recurrence extension""" l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, @@ -4657,6 +4693,7 @@ def test_fetchinvoice_recurrence(node_factory, bitcoind): 'recurrence_label': 'test paywindow'}) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "invoice from offer: Invalid bech32: invalid checksum") def test_fetchinvoice_autoconnect(node_factory, bitcoind): """We should autoconnect if we need to, to route.""" @@ -4733,6 +4770,7 @@ def test_dev_rawrequest(node_factory): assert 'invoice' in ret +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "sendinvoice: bolt12: Invalid bech32: invalid checksum") def test_sendinvoice(node_factory, bitcoind): l2opts = {'experimental-offers': None} l1, l2 = node_factory.line_graph(2, wait_for_announce=True, @@ -5170,6 +5208,7 @@ def test_payerkey(node_factory): assert n.rpc.decode(b12)['invreq_payer_id'] == k +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "tried commitment when funding is not buried ") def test_pay_multichannel_use_zeroconf(bitcoind, node_factory): """Check that we use the zeroconf direct channel to pay when we need to""" # 0. Setup normal channel, 200k sats. diff --git a/tests/test_plugin.py b/tests/test_plugin.py index e86bc18df052..8f0b440e6a99 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -705,6 +705,7 @@ def test_openchannel_hook(node_factory, bitcoind): l1.rpc.fundchannel(l2.info['id'], 100001) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "this test frequently hangs w/ VLSD") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_openchannel_hook_error_handling(node_factory, bitcoind): @@ -1341,12 +1342,14 @@ def test_forward_event_notification(node_factory, bitcoind, executor): route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] # status: offered -> settled + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash13, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash13) # status: offered -> failed route = l1.rpc.getroute(l4.info['id'], amount, 1)['route'] payment_hash14 = "f" * 64 + l1.rpc.preapprovekeysend(l4.info['id'], payment_hash14, amount) with pytest.raises(RpcError): l1.rpc.sendpay(route, payment_hash14, payment_secret="f" * 64) l1.rpc.waitsendpay(payment_hash14) @@ -1366,6 +1369,7 @@ def test_forward_event_notification(node_factory, bitcoind, executor): 'delay': 5, 'channel': c25}] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming executor.submit(l1.rpc.sendpay, route, payment_hash15, payment_secret=inv['payment_secret']) l5.daemon.wait_for_log('permfail') @@ -1452,11 +1456,13 @@ def test_sendpay_notifications(node_factory, bitcoind): payment_hash2 = inv2['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] + l1.rpc.preapproveinvoice(bolt11=inv1['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash1, payment_secret=inv1['payment_secret']) response1 = l1.rpc.waitsendpay(payment_hash1) l2.rpc.close(chanid23, 1) + l1.rpc.preapproveinvoice(bolt11=inv2['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash2, payment_secret=inv2['payment_secret']) with pytest.raises(RpcError) as err: l1.rpc.waitsendpay(payment_hash2) @@ -1483,11 +1489,13 @@ def test_sendpay_notifications_nowaiter(node_factory): payment_hash2 = inv2['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] + l1.rpc.preapproveinvoice(bolt11=inv1['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash1, payment_secret=inv1['payment_secret']) l1.daemon.wait_for_log(r'Received a sendpay_success') l2.rpc.close(chanid23, 1) + l1.rpc.preapproveinvoice(bolt11=inv2['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash2, payment_secret=inv2['payment_secret']) l1.daemon.wait_for_log(r'Received a sendpay_failure') @@ -1778,6 +1786,7 @@ def test_bitcoin_backend(node_factory, bitcoind): " bitcoind") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "channel too big, then feerate above maximum") def test_bitcoin_bad_estimatefee(node_factory, bitcoind): """ This tests that we don't crash if bitcoind backend gives bad estimatefees. @@ -1948,6 +1957,7 @@ def test_replacement_payload(node_factory): assert l2.daemon.wait_for_log("Attempt to pay.*with wrong secret") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "dev_sign_last_tx causes subsequent validate_holder_commitment_tx failure") def test_watchtower(node_factory, bitcoind, directory, chainparams): """Test watchtower hook. @@ -2087,12 +2097,14 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] # status: offered -> settled + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash13, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash13) # status: offered -> failed route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] payment_hash13 = "f" * 64 + l1.rpc.preapprovekeysend(l3.info['id'], payment_hash13, amount) with pytest.raises(RpcError): l1.rpc.sendpay(route, payment_hash13, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash13) @@ -2101,6 +2113,7 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): inv = l1.rpc.invoice(amount // 2, "first", "desc") payment_hash31 = inv['payment_hash'] route = l3.rpc.getroute(l1.info['id'], amount // 2, 1)['route'] + l3.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l3.rpc.sendpay(route, payment_hash31, payment_secret=inv['payment_secret']) l3.rpc.waitsendpay(payment_hash31) @@ -2108,6 +2121,7 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): inv = l2.rpc.invoice(amount, "first", "desc") payment_hash12 = inv['payment_hash'] route = l1.rpc.getroute(l2.info['id'], amount, 1)['route'] + l1.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l1.rpc.sendpay(route, payment_hash12, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash12) @@ -2117,6 +2131,7 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): # Make sure previous completely settled wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['htlcs'] == []) route = l2.rpc.getroute(l1.info['id'], amount // 2, 1)['route'] + l2.rpc.preapproveinvoice(bolt11=inv['bolt11']) # let the signer know this payment is coming l2.rpc.sendpay(route, payment_hash21, payment_secret=inv['payment_secret']) l2.rpc.waitsendpay(payment_hash21) @@ -2161,6 +2176,7 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): check_coin_moves(l2, chanid_3, l2_l3_mvts, chainparams) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "trouble managing remotesigner when node killed this way") def test_important_plugin(node_factory): # Cache it here. pluginsdir = os.path.join(os.path.dirname(__file__), "plugins") @@ -3392,6 +3408,7 @@ def test_block_added_notifications(node_factory, bitcoind): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "commit c0cc285a causes: channel stub can only return point for commitment number zero") def test_sql(node_factory, bitcoind): opts = {'experimental-offers': None, 'experimental-dual-fund': None, diff --git a/tests/test_renepay.py b/tests/test_renepay.py index f2b8f76f8ce2..86b196210a78 100644 --- a/tests/test_renepay.py +++ b/tests/test_renepay.py @@ -6,6 +6,7 @@ import time import json import subprocess +import unittest def test_simple(node_factory): @@ -72,6 +73,8 @@ def test_mpp(node_factory): send_amount = Millisatoshi('1200000sat') inv = l6.rpc.invoice(send_amount, 'test_renepay', 'description')['bolt11'] + # FIXME - This shouldn't be necessary, renepay should automatically generate ... vls-hsmd:#6 + l1.rpc.preapproveinvoice(bolt11=inv) # let the signer know this payment is coming details = l1.rpc.call('renepay', {'invstring': inv}) assert details['status'] == 'complete' assert details['amount_msat'] == send_amount @@ -215,6 +218,7 @@ def test_amounts(node_factory): assert invoice['amount_received_msat'] >= Millisatoshi(123456) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "invoice with any amount") def test_limits(node_factory): ''' Topology: @@ -312,7 +316,7 @@ def start_channels(connections): for n2 in nodes: wait_for(lambda: 'alias' in only_one(n.rpc.listnodes(n2.info['id'])['nodes'])) - +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "invoice with any amount") def test_hardmpp(node_factory): ''' Topology: diff --git a/tests/test_splicing.py b/tests/test_splicing.py index 424b49c00f4b..6c09bf7ba0ea 100644 --- a/tests/test_splicing.py +++ b/tests/test_splicing.py @@ -9,10 +9,12 @@ ) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') -@flaky +# incompatible w/ pytest-timeout +# @flaky def test_splice(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None}) @@ -47,6 +49,7 @@ def test_splice(node_factory, bitcoind): assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @@ -105,6 +108,7 @@ def test_splice_gossip(node_factory, bitcoind): assert not l2.daemon.is_in_log("invalid local_channel_announcement") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @@ -140,6 +144,7 @@ def test_splice_listnodes(node_factory, bitcoind): assert len(l2.rpc.listnodes()['nodes']) == 2 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @@ -176,6 +181,7 @@ def test_splice_out(node_factory, bitcoind): assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @@ -211,13 +217,14 @@ def test_invalid_splice(node_factory, bitcoind): 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()) + # Wait until nodes are reconnected + l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + bitcoind.generate_block(6, wait_for_mempool=1) l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') @@ -286,6 +293,7 @@ def test_commit_crash_splice(node_factory, bitcoind): assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0 +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') diff --git a/tests/test_splicing_disconnect.py b/tests/test_splicing_disconnect.py new file mode 100644 index 000000000000..b2249acd99f2 --- /dev/null +++ b/tests/test_splicing_disconnect.py @@ -0,0 +1,121 @@ +from fixtures import * # noqa: F401,F403 +import pytest +import unittest +import time +from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND +from utils import ( + TEST_NETWORK, first_scid +) + + +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +def test_splice_disconnect_sig(node_factory, bitcoind): + # Dual open and splicing both use tx_sig messages. If we have dual enabled, ignore the first one. + disconnect = ['-WIRE_TX_SIGNATURES'] + if EXPERIMENTAL_DUAL_FUND: + disconnect = ['=WIRE_TX_SIGNATURES'] + disconnect + + l1 = node_factory.get_node(disconnect=disconnect, + options={'experimental-splicing': None, 'dev-no-reconnect': None}, + may_reconnect=True) + l2 = node_factory.get_node(options={'experimental-splicing': None}, may_reconnect=True) + l1.openchannel(l2, 1000000) + + 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']) + + l1.daemon.wait_for_log(r'dev_disconnect: \-WIRE_TX_SIGNATURES') + + print("Killing l1 without sending WIRE_TX_SIGNATURES") + l1.daemon.kill() + + # Restart l1, without disconnect stuff. + del l1.daemon.opts['dev-no-reconnect'] + del l1.daemon.opts['dev-disconnect'] + + # Should reconnect, and reestablish the splice. + l1.start() + + # Wait until nodes are reconnected + l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + + bitcoind.generate_block(6, wait_for_mempool=1) + + l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l1.daemon.wait_for_log(r'private channel announcement from channeld for ' + first_scid(l1, l2)) + + inv = l2.rpc.invoice(10**2, '3', 'no_3') + l1.rpc.pay(inv['bolt11']) + + # Check that the splice doesn't generate a unilateral close transaction + time.sleep(5) + assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0 + + +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_SKIP_SPLICE_TESTS') == '1', "test expected to fail before VLS dual-funding / splicing support") +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +def test_splice_disconnect_commit(node_factory, bitcoind, executor): + l1 = node_factory.get_node(options={'experimental-splicing': None}, may_reconnect=True) + l2 = node_factory.get_node(disconnect=['+WIRE_COMMITMENT_SIGNED'], + options={'experimental-splicing': None, 'dev-no-reconnect': None}, + may_reconnect=True) + l1.openchannel(l2, 1000000) + + 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']) + print("l1 splice_update") + result = l1.rpc.splice_update(chan_id, result['psbt']) + print("l1 signpsbt") + result = l1.rpc.signpsbt(result['psbt']) + print("l1 splice_signed") + + executor.submit(l1.rpc.splice_signed, chan_id, result['signed_psbt']) + + print("l2 waiting for dev_disconnect msg") + + l2.daemon.wait_for_log(r'dev_disconnect: \+WIRE_COMMITMENT_SIGNED') + + print("Killing l2 without sending WIRE_COMMITMENT_SIGNED") + l2.daemon.kill() + + # Restart l1, without disconnect stuff. + del l2.daemon.opts['dev-no-reconnect'] + del l2.daemon.opts['dev-disconnect'] + + # Should reconnect, and reestablish the splice. + l2.start() + + # Wait until nodes are reconnected + l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + + bitcoind.generate_block(6, wait_for_mempool=1) + + l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l1.daemon.wait_for_log(r'private channel announcement from channeld for ' + first_scid(l1, l2)) + + inv = l2.rpc.invoice(10**2, '3', 'no_3') + l1.rpc.pay(inv['bolt11']) + + # Check that the splice doesn't generate a unilateral close transaction + time.sleep(5) + assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0 diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 40d078cd338d..9da42d233c11 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -782,6 +782,7 @@ def test_utxopsbt(node_factory, bitcoind, chainparams): reservedok=True) +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd') and os.getenv('VLS_PERMISSIVE') != '1', "non-beneficial value considered as fees is above maximum feerate") def test_sign_external_psbt(node_factory, bitcoind, chainparams): """ A PSBT w/ one of our inputs should be signable (we can fill @@ -859,6 +860,7 @@ def test_psbt_version(node_factory, bitcoind, chainparams): @unittest.skipIf(TEST_NETWORK == 'liquid-regtest', 'Core/Elements need joinpsbt support for v2') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "policy: can't withdraw to non-wallet address") # FIXME - should work with VLS_PERMISSIVE def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): """ Tests for the sign + send psbt RPCs @@ -1117,6 +1119,7 @@ def write_all(fd, bytestr): @unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support hsm_secret file") def test_hsm_secret_encryption(node_factory): l1 = node_factory.get_node(may_fail=True) # May fail when started without key password = "reckful&é🍕\n" @@ -1180,6 +1183,7 @@ def __init__(self, directory, *args): @unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.") +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support hsm_secret file") def test_hsmtool_secret_decryption(node_factory): l1 = node_factory.get_node() password = "reckless123#{ù}\n" @@ -1316,6 +1320,7 @@ def test_hsmtool_dump_descriptors(node_factory, bitcoind): assert res["total_amount"] == Decimal('0.00001000') +@unittest.skipIf(os.getenv('SUBDAEMON').startswith('hsmd:remote_hsmd'), "remote_hsmd doesn't support generatehsm") def test_hsmtool_generatehsm(node_factory): l1 = node_factory.get_node(start=False) hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, diff --git a/wallet/db.c b/wallet/db.c index 376b4bee7cef..33e322aafd36 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1010,6 +1010,7 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE forwards ADD updated_index BIGINT DEFAULT 0"), NULL}, {SQL("CREATE INDEX forwards_updated_idx ON forwards (updated_index)"), NULL}, {NULL, migrate_initialize_forwards_wait_indexes}, + {SQL("ALTER TABLE channel_funding_inflights ADD force_sign_first INTEGER DEFAULT 0"), NULL}, }; /** diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index ca7ca447626c..ddabea496a5d 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -201,7 +201,8 @@ struct channel_inflight *new_inflight(struct channel *channel UNNEEDED, const struct amount_msat lease_fee UNNEEDED, const struct amount_sat lease_amt UNNEEDED, s64 splice_amnt UNNEEDED, - bool i_am_initiator UNNEEDED) + bool i_am_initiator UNNEEDED, + bool force_sign_first 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 26fda1b90cc6..42cec47f1ef0 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1923,6 +1923,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) AMOUNT_MSAT(10), AMOUNT_SAT(1111), 0, + false, false); inflight_set_last_tx(inflight, last_tx, sig); @@ -1949,6 +1950,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) AMOUNT_MSAT(0), AMOUNT_SAT(0), 0, + false, false); inflight_set_last_tx(inflight, last_tx, sig); wallet_inflight_add(w, inflight); diff --git a/wallet/wallet.c b/wallet/wallet.c index ddad3b5c4356..e2e7986594a6 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1217,8 +1217,9 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) ", lease_satoshi" ", splice_amnt" ", i_am_initiator" + ", force_sign_first" ") VALUES (" - "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, inflight->channel->dbid); db_bind_txid(stmt, &inflight->funding->outpoint.txid); @@ -1256,6 +1257,7 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) db_bind_s64(stmt, inflight->funding->splice_amnt); db_bind_int(stmt, inflight->i_am_initiator); + db_bind_int(stmt, inflight->force_sign_first); db_exec_prepared_v2(stmt); assert(!stmt->error); @@ -1324,7 +1326,7 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, struct bitcoin_tx *last_tx; struct channel_inflight *inflight; s64 splice_amnt; - bool i_am_initiator; + bool i_am_initiator, force_sign_first; secp256k1_ecdsa_signature *lease_commit_sig; u32 lease_blockheight_start; @@ -1362,6 +1364,7 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, splice_amnt = db_col_s64(stmt, "splice_amnt"); i_am_initiator = db_col_int(stmt, "i_am_initiator"); + force_sign_first = db_col_int(stmt, "force_sign_first"); inflight = new_inflight(chan, &funding, db_col_int(stmt, "funding_feerate"), @@ -1376,7 +1379,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, lease_fee, lease_amt, splice_amnt, - i_am_initiator); + i_am_initiator, + force_sign_first); /* last_tx is null for not yet committed * channels + static channel backup recoveries */ @@ -1427,6 +1431,7 @@ static bool wallet_channel_load_inflights(struct wallet *w, ", lease_satoshi" ", splice_amnt" ", i_am_initiator" + ", force_sign_first" " FROM channel_funding_inflights" " WHERE channel_id = ?" " ORDER BY funding_feerate"));