From d586d9a4ead027ce3b01b0df61af35eb5d8bc082 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:45:10 +1030 Subject: [PATCH 01/14] lightningd: prepare internal json routines for listpeerchannels. We're soon going to call json_add_unsaved_channel and json_add_uncommitted_channel from a new place, where we want the peer state directly included. Based on patch by @vincenzopalazzo. Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 9 ++++++++- lightningd/dual_open_control.h | 4 +++- lightningd/opening_control.c | 9 ++++++++- lightningd/opening_control.h | 4 +++- lightningd/peer_control.c | 14 ++++++++++---- lightningd/test/run-invoice-select-inchan.c | 8 ++++++-- wallet/test/run-wallet.c | 8 ++++++-- 7 files changed, 44 insertions(+), 12 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index c65b2bd29de8..428c1ede1984 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -105,7 +105,9 @@ static void channel_err_broken(struct channel *channel, } void json_add_unsaved_channel(struct json_stream *response, - const struct channel *channel) + const struct channel *channel, + /* Only set for listpeerchannels */ + const struct peer *peer) { struct amount_msat total; struct open_attempt *oa; @@ -125,6 +127,11 @@ void json_add_unsaved_channel(struct json_stream *response, oa = channel->open_attempt; json_object_start(response, NULL); + /* listpeerchannels only */ + if (peer) { + json_add_node_id(response, "peer_id", &peer->id); + json_add_bool(response, "peer_connected", peer->connected == PEER_CONNECTED); + } json_add_string(response, "state", channel_state_name(channel)); json_add_string(response, "owner", channel->owner->name); json_add_string(response, "opener", channel->opener == LOCAL ? diff --git a/lightningd/dual_open_control.h b/lightningd/dual_open_control.h index 7d34d9816c57..63c51bc3f3df 100644 --- a/lightningd/dual_open_control.h +++ b/lightningd/dual_open_control.h @@ -22,7 +22,9 @@ void dualopen_tell_depth(struct subd *dualopend, void channel_unsaved_close_conn(struct channel *channel, const char *why); void json_add_unsaved_channel(struct json_stream *response, - const struct channel *channel); + const struct channel *channel, + /* Only set for listpeerchannels */ + const struct peer *peer); void channel_update_reserve(struct channel *channel, struct channel_config *their_config, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index a64764c8577e..2aebaec623c9 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -32,9 +32,12 @@ #include void json_add_uncommitted_channel(struct json_stream *response, - const struct uncommitted_channel *uc) + const struct uncommitted_channel *uc, + /* Only set for listpeerchannels */ + const struct peer *peer) { struct amount_msat total, ours; + if (!uc) return; @@ -43,6 +46,10 @@ void json_add_uncommitted_channel(struct json_stream *response, return; json_object_start(response, NULL); + if (peer) { + json_add_node_id(response, "peer_id", &peer->id); + json_add_bool(response, "peer_connected", peer->connected == PEER_CONNECTED); + } json_add_string(response, "state", "OPENINGD"); json_add_string(response, "owner", "lightning_openingd"); json_add_string(response, "opener", "local"); diff --git a/lightningd/opening_control.h b/lightningd/opening_control.h index a8f8d982a73a..258735713159 100644 --- a/lightningd/opening_control.h +++ b/lightningd/opening_control.h @@ -12,7 +12,9 @@ struct peer_fd; struct uncommitted_channel; void json_add_uncommitted_channel(struct json_stream *response, - const struct uncommitted_channel *uc); + const struct uncommitted_channel *uc, + /* Only set for listpeerchannels */ + const struct peer *peer); bool peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 76f5a768ec66..7b18d2cc1609 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -695,7 +695,9 @@ struct amount_msat channel_amount_receivable(const struct channel *channel) static void json_add_channel(struct lightningd *ld, struct json_stream *response, const char *key, - const struct channel *channel) + const struct channel *channel, + /* Only set for listpeerchannels */ + const struct peer *peer) { struct channel_stats channel_stats; struct amount_msat funding_msat; @@ -704,6 +706,10 @@ static void json_add_channel(struct lightningd *ld, u32 feerate; json_object_start(response, key); + if (peer) { + json_add_node_id(response, "peer_id", &peer->id); + json_add_bool(response, "peer_connected", peer->connected == PEER_CONNECTED); + } json_add_string(response, "state", channel_state_name(channel)); if (channel->last_tx && !invalid_last_tx(channel->last_tx)) { struct bitcoin_txid txid; @@ -1936,13 +1942,13 @@ static void json_add_peer(struct lightningd *ld, } json_array_start(response, "channels"); - json_add_uncommitted_channel(response, p->uncommitted_channel); + json_add_uncommitted_channel(response, p->uncommitted_channel, NULL); list_for_each(&p->channels, channel, list) { if (channel_unsaved(channel)) - json_add_unsaved_channel(response, channel); + json_add_unsaved_channel(response, channel, NULL); else - json_add_channel(ld, response, NULL, channel); + json_add_channel(ld, response, NULL, channel, NULL); } json_array_end(response); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index e1aee3435f68..0d3dfa82cfbc 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -445,11 +445,15 @@ void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNN { fprintf(stderr, "json_add_u64 called!\n"); abort(); } /* Generated stub for json_add_uncommitted_channel */ void json_add_uncommitted_channel(struct json_stream *response UNNEEDED, - const struct uncommitted_channel *uc UNNEEDED) + const struct uncommitted_channel *uc UNNEEDED, + /* Only set for listpeerchannels */ + const struct peer *peer UNNEEDED) { fprintf(stderr, "json_add_uncommitted_channel called!\n"); abort(); } /* Generated stub for json_add_unsaved_channel */ void json_add_unsaved_channel(struct json_stream *response UNNEEDED, - const struct channel *channel UNNEEDED) + const struct channel *channel UNNEEDED, + /* Only set for listpeerchannels */ + const struct peer *peer UNNEEDED) { fprintf(stderr, "json_add_unsaved_channel called!\n"); abort(); } /* Generated stub for json_array_end */ void json_array_end(struct json_stream *js UNNEEDED) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index e470a61e5bc0..4421c59ae0aa 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -403,11 +403,15 @@ void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNN { fprintf(stderr, "json_add_u64 called!\n"); abort(); } /* Generated stub for json_add_uncommitted_channel */ void json_add_uncommitted_channel(struct json_stream *response UNNEEDED, - const struct uncommitted_channel *uc UNNEEDED) + const struct uncommitted_channel *uc UNNEEDED, + /* Only set for listpeerchannels */ + const struct peer *peer UNNEEDED) { fprintf(stderr, "json_add_uncommitted_channel called!\n"); abort(); } /* Generated stub for json_add_unsaved_channel */ void json_add_unsaved_channel(struct json_stream *response UNNEEDED, - const struct channel *channel UNNEEDED) + const struct channel *channel UNNEEDED, + /* Only set for listpeerchannels */ + const struct peer *peer UNNEEDED) { fprintf(stderr, "json_add_unsaved_channel called!\n"); abort(); } /* Generated stub for json_array_end */ void json_array_end(struct json_stream *js UNNEEDED) From 8e69aab89250c01fe95f6b48f0498b3f4fcfe8c3 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 12 Jan 2023 11:46:10 +1030 Subject: [PATCH 02/14] lightningd: add listpeerchannels command Changelog-Added: JSON-RPC: new command `listpeerchannels` now contains information on direct channels with our peers. Signed-off-by: Vincenzo Palazzo --- contrib/pyln-client/pyln/client/lightning.py | 10 + doc/Makefile | 1 + doc/index.rst | 1 + doc/lightning-listpeerchannels.7.md | 345 +++++++ doc/schemas/listpeerchannels.request.json | 12 + doc/schemas/listpeerchannels.schema.json | 980 +++++++++++++++++++ lightningd/peer_control.c | 54 + 7 files changed, 1403 insertions(+) create mode 100644 doc/lightning-listpeerchannels.7.md create mode 100644 doc/schemas/listpeerchannels.request.json create mode 100644 doc/schemas/listpeerchannels.schema.json diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index d65640256d96..15272b1d5a76 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -1070,6 +1070,16 @@ def listpeers(self, peerid=None, level=None): } return self.call("listpeers", payload) + def listpeerchannels(self, peer_id=None): + """ + Show current peers channels, and if the {peer_id} is specified + all the channels for the peer are returned. + """ + payload = { + "id": peer_id, + } + return self.call("listpeerchannels", payload) + def listsendpays(self, bolt11=None, payment_hash=None, status=None): """Show all sendpays results, or only for `bolt11` or `payment_hash`.""" payload = { diff --git a/doc/Makefile b/doc/Makefile index f4d6a4744c06..da1e85242343 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -58,6 +58,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-listoffers.7 \ doc/lightning-listpays.7 \ doc/lightning-listpeers.7 \ + doc/lightning-listpeerchannels.7 \ doc/lightning-listsendpays.7 \ doc/lightning-makesecret.7 \ doc/lightning-multifundchannel.7 \ diff --git a/doc/index.rst b/doc/index.rst index 68498827ae7c..687e8cb95c89 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -85,6 +85,7 @@ Core Lightning Documentation lightning-listnodes lightning-listoffers lightning-listpays + lightning-listpeerchannels lightning-listpeers lightning-listsendpays lightning-listtransactions diff --git a/doc/lightning-listpeerchannels.7.md b/doc/lightning-listpeerchannels.7.md new file mode 100644 index 000000000000..d20514eaffdd --- /dev/null +++ b/doc/lightning-listpeerchannels.7.md @@ -0,0 +1,345 @@ +lightning-listpeerchannels -- Command returning data on channels of connected lightning nodes +========================================================================== + +SYNOPSIS +-------- + +**listpeerchannels** \[*id*\] + +DESCRIPTION +----------- + +The **listpeerchannels** RPC command returns data on channels of the network, with the possibility to filter the channels by node id. + +If no *id* is supplied, then channel data on all lightning nodes that are +connected, or not connected but have open channels with this node, are +returned. + +Supplying *id* will filter the results to only return channel data that match *id*, +if one exists. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **channels** is returned. It is an array of objects, where each object contains: + +- **peer\_id** (pubkey): Node Public key +- **peer\_connected** (boolean): A boolean flag that is set to true if the peer is online +- **state** (string): the channel state, in particular "CHANNELD\_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN") +- **opener** (string): Who initiated the channel (one of "local", "remote") +- **features** (array of strings): + - BOLT #9 features which apply to this channel (one of "option\_static\_remotekey", "option\_anchor\_outputs", "option\_zeroconf") +- **scratch\_txid** (txid, optional): The txid we would use if we went onchain now +- **feerate** (object, optional): Feerates for the current tx: + - **perkw** (u32): Feerate per 1000 weight (i.e kSipa) + - **perkb** (u32): Feerate per 1000 virtual bytes +- **owner** (string, optional): The current subdaemon controlling this connection +- **short\_channel\_id** (short\_channel\_id, optional): The short\_channel\_id (once locked in) +- **channel\_id** (hash, optional): The full channel\_id (always 64 characters) +- **funding\_txid** (txid, optional): ID of the funding transaction +- **funding\_outnum** (u32, optional): The 0-based output number of the funding transaction which opens the channel +- **initial\_feerate** (string, optional): For inflight opens, the first feerate used to initiate the channel open +- **last\_feerate** (string, optional): For inflight opens, the most recent feerate used on the channel open +- **next\_feerate** (string, optional): For inflight opens, the next feerate we'll use for the channel open +- **next\_fee\_step** (u32, optional): For inflight opens, the next feerate step we'll use for the channel open +- **inflight** (array of objects, optional): Current candidate funding transactions (only for dual-funding): + - **funding\_txid** (txid): ID of the funding transaction + - **funding\_outnum** (u32): The 0-based output number of the funding transaction which opens the channel + - **feerate** (string): The feerate for this funding transaction in per-1000-weight, with "kpw" appended + - **total\_funding\_msat** (msat): total amount in the channel + - **our\_funding\_msat** (msat): amount we have in the channel + - **scratch\_txid** (txid): The commitment transaction txid we would use if we went onchain now +- **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close +- **private** (boolean, optional): if False, we will not announce this channel +- **closer** (string, optional): Who initiated the channel close (one of "local", "remote") +- **funding** (object, optional): + - **local\_funds\_msat** (msat): Amount of channel we funded + - **remote\_funds\_msat** (msat): Amount of channel they funded + - **local\_msat** (msat, optional): Amount of channel we funded (deprecated) + - **remote\_msat** (msat, optional): Amount of channel they funded (deprecated) + - **pushed\_msat** (msat, optional): Amount pushed from opener to peer + - **fee\_paid\_msat** (msat, optional): Amount we paid peer at open + - **fee\_rcvd\_msat** (msat, optional): Amount we were paid by peer at open +- **to\_us\_msat** (msat, optional): how much of channel is owed to us +- **min\_to\_us\_msat** (msat, optional): least amount owed to us ever +- **max\_to\_us\_msat** (msat, optional): most amount owed to us ever +- **total\_msat** (msat, optional): total amount in the channel +- **fee\_base\_msat** (msat, optional): amount we charge to use the channel +- **fee\_proportional\_millionths** (u32, optional): amount we charge to use the channel in parts-per-million +- **dust\_limit\_msat** (msat, optional): minimum amount for an output on the channel transactions +- **max\_total\_htlc\_in\_msat** (msat, optional): max amount accept in a single payment +- **their\_reserve\_msat** (msat, optional): minimum we insist they keep in channel +- **our\_reserve\_msat** (msat, optional): minimum they insist we keep in channel +- **spendable\_msat** (msat, optional): total we could send through channel +- **receivable\_msat** (msat, optional): total peer could send through channel +- **minimum\_htlc\_in\_msat** (msat, optional): the minimum amount HTLC we accept +- **minimum\_htlc\_out\_msat** (msat, optional): the minimum amount HTLC we will send +- **maximum\_htlc\_out\_msat** (msat, optional): the maximum amount HTLC we will send +- **their\_to\_self\_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close +- **our\_to\_self\_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close +- **max\_accepted\_htlcs** (u32, optional): Maximum number of incoming HTLC we will accept at once +- **alias** (object, optional): + - **local** (short\_channel\_id, optional): An alias assigned by this node to this channel, used for outgoing payments + - **remote** (short\_channel\_id, optional): An alias assigned by the remote node to this channel, usable in routehints and invoices +- **state\_changes** (array of objects, optional): Prior state changes: + - **timestamp** (string): UTC timestamp of form YYYY-mm-ddTHH:MM:SS.%03dZ + - **old\_state** (string): Previous state (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN") + - **new\_state** (string): New state (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN") + - **cause** (string): What caused the change (one of "unknown", "local", "user", "remote", "protocol", "onchain") + - **message** (string): Human-readable explanation +- **status** (array of strings, optional): + - Billboard log of significant changes +- **in\_payments\_offered** (u64, optional): Number of incoming payment attempts +- **in\_offered\_msat** (msat, optional): Total amount of incoming payment attempts +- **in\_payments\_fulfilled** (u64, optional): Number of successful incoming payment attempts +- **in\_fulfilled\_msat** (msat, optional): Total amount of successful incoming payment attempts +- **out\_payments\_offered** (u64, optional): Number of outgoing payment attempts +- **out\_offered\_msat** (msat, optional): Total amount of outgoing payment attempts +- **out\_payments\_fulfilled** (u64, optional): Number of successful outgoing payment attempts +- **out\_fulfilled\_msat** (msat, optional): Total amount of successful outgoing payment attempts +- **htlcs** (array of objects, optional): current HTLCs in this channel: + - **direction** (string): Whether it came from peer, or is going to peer (one of "in", "out") + - **id** (u64): Unique ID for this htlc on this channel in this direction + - **amount\_msat** (msat): Amount send/received for this HTLC + - **expiry** (u32): Block this HTLC expires at + - **payment\_hash** (hash): the hash of the payment\_preimage which will prove payment (always 64 characters) + - **local\_trimmed** (boolean, optional): if this is too small to enforce onchain (always *true*) + - **status** (string, optional): set if this HTLC is currently waiting on a hook (and shows what plugin) + + If **direction** is "out": + + - **state** (string): Status of the HTLC (one of "SENT\_ADD\_HTLC", "SENT\_ADD\_COMMIT", "RCVD\_ADD\_REVOCATION", "RCVD\_ADD\_ACK\_COMMIT", "SENT\_ADD\_ACK\_REVOCATION", "RCVD\_REMOVE\_HTLC", "RCVD\_REMOVE\_COMMIT", "SENT\_REMOVE\_REVOCATION", "SENT\_REMOVE\_ACK\_COMMIT", "RCVD\_REMOVE\_ACK\_REVOCATION") + + If **direction** is "in": + + - **state** (string): Status of the HTLC (one of "RCVD\_ADD\_HTLC", "RCVD\_ADD\_COMMIT", "SENT\_ADD\_REVOCATION", "SENT\_ADD\_ACK\_COMMIT", "RCVD\_ADD\_ACK\_REVOCATION", "SENT\_REMOVE\_HTLC", "SENT\_REMOVE\_COMMIT", "RCVD\_REMOVE\_REVOCATION", "RCVD\_REMOVE\_ACK\_COMMIT", "SENT\_REMOVE\_ACK\_REVOCATION") + +If **close\_to** is present: + + - **close\_to\_addr** (string, optional): The bitcoin address we will close to + +If **scratch\_txid** is present: + + - **last\_tx\_fee\_msat** (msat): fee attached to this the current tx + +If **short\_channel\_id** is present: + + - **direction** (u32): 0 if we're the lesser node\_id, 1 if we're the greater + +If **inflight** is present: + + - **initial\_feerate** (string): The feerate for the initial funding transaction in per-1000-weight, with "kpw" appended + - **last\_feerate** (string): The feerate for the latest funding transaction in per-1000-weight, with "kpw" appended + - **next\_feerate** (string): The minimum feerate for the next funding transaction in per-1000-weight, with "kpw" appended + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +On success, an object with a "channels" key is returned containing a list +of 0 or more objects. If *id* and/or *status* are supplied and no matching +nodes are found, a "channels" object with an empty list is returned. + +The objects in the *channels* array will have at least these fields: + +* *state*: Any of these strings: + * `"OPENINGD"`: The channel funding protocol with the peer is ongoing + and both sides are negotiating parameters. + * `"CHANNELD_AWAITING_LOCKIN"`: The peer and you have agreed on channel + parameters and are just waiting for the channel funding transaction to + be confirmed deeply. + Both you and the peer must acknowledge the channel funding transaction + to be confirmed deeply before entering the next state. + * `"CHANNELD_NORMAL"`: The channel can be used for normal payments. + * `"CHANNELD_SHUTTING_DOWN"`: A mutual close was requested (by you or + peer) and both of you are waiting for HTLCs in-flight to be either + failed or succeeded. + The channel can no longer be used for normal payments and forwarding. + Mutual close will proceed only once all HTLCs in the channel have + either been fulfilled or failed. + * `"CLOSINGD_SIGEXCHANGE"`: You and the peer are negotiating the mutual + close onchain fee. + * `"CLOSINGD_COMPLETE"`: You and the peer have agreed on the mutual close + onchain fee and are awaiting the mutual close getting confirmed deeply. + * `"AWAITING_UNILATERAL"`: You initiated a unilateral close, and are now + waiting for the peer-selected unilateral close timeout to complete. + * `"FUNDING_SPEND_SEEN"`: You saw the funding transaction getting + spent (usually the peer initiated a unilateral close) and will now + determine what exactly happened (i.e. if it was a theft attempt). + * `"ONCHAIN"`: You saw the funding transaction getting spent and now + know what happened (i.e. if it was a proper unilateral close by the + peer, or a theft attempt). + * `"CLOSED"`: The channel closure has been confirmed deeply. + The channel will eventually be removed from this array. +* *state\_changes*: An array of objects describing prior state change events. +* *opener*: A string `"local"` or `"remote`" describing which side opened this + channel. +* *closer*: A string `"local"` or `"remote`" describing which side + closed this channel or `null` if the channel is not (being) closed yet. +* *status*: An array of strings containing the most important log messages + relevant to this channel. + Also known as the "billboard". +* *owner*: A string describing which particular sub-daemon of `lightningd` + currently is responsible for this channel. + One of: `"lightning_openingd"`, `"lightning_channeld"`, + `"lightning_closingd"`, `"lightning_onchaind"`. +* *to\_us\_msat*: A string describing how much of the funds is owned by us; + a number followed by a string unit. +* *total\_msat*: A string describing the total capacity of the channel; + a number followed by a string unit. +* *fee\_base\_msat*: The fixed routing fee we charge for forwards going out over + this channel, regardless of payment size. +* *fee\_proportional\_millionths*: The proportional routing fees in ppm (parts- + per-millionths) we charge for forwards going out over this channel. +* *features*: An array of feature names supported by this channel. + +These fields may exist if the channel has gotten beyond the `"OPENINGD"` +state, or in various circumstances: + +* *short\_channel\_id*: A string of the short channel ID for the channel; + Format is `"BBBBxTTTxOOO"`, where `"BBBB"` is the numeric block height + at which the funding transaction was confirmed, `"TTT"` is the numeric + funding transaction index within that block, and `"OOO"` is the + numeric output index of the transaction output that actually anchors + this channel. +* *direction*: The channel-direction we own, as per BOLT \#7. + We own channel-direction 0 if our node ID is "less than" the peer node ID + in a lexicographical ordering of our node IDs, otherwise we own + channel-direction 1. + Our `channel_update` will use this *direction*. +* *channel\_id*: The full channel ID of the channel; + the funding transaction ID XORed with the output number. +* *funding\_txid*: The funding transaction ID of the channel. +* *close\_to*: The raw `scriptPubKey` that was indicated in the starting + **fundchannel\_start** command and accepted by the peer. + If the `scriptPubKey` encodes a standardized address, an additional + *close\_to\_addr* field will be present with the standardized address. +* *private*: A boolean, true if the channel is unpublished, false if the + channel is published. +* *funding\_msat*: An object, whose field names are the node + IDs involved in the channel, and whose values are strings (numbers with + a unit suffix) indicating how much that node originally contributed in + opening the channel. +* *min\_to\_us\_msat*: A string describing the historic point at which + we owned the least amount of funds in this channel; + a number followed by a string unit. + If the peer were to succesfully steal from us, this is the amount we + would still retain. +* *max\_to\_us\_msat*: A string describing the historic point at which + we owned the most amount of funds in this channel; + a number followed by a string unit. + If we were to successfully steal from the peer, this is the amount we + could potentially get. +* *dust\_limit\_msat*: A string describing an amount; + if an HTLC or the amount wholly-owned by one node is at or below this + amount, it will be considered "dusty" and will not appear in a close + transaction, and will be donated to miners as fee; + a number followed by a string unit. +* *max\_total\_htlc\_in\_msat*: A string describing an amount; + the sum of all HTLCs in the channel cannot exceed this amount; + a number followed by a string unit. +* *their\_reserve\_msat*: A string describing the minimum amount that + the peer must keep in the channel when it attempts to send out; + if it has less than this in the channel, it cannot send to us on + that channel; + a number followed by a string unit. + We impose this on them, default is 1% of the total channel capacity. +* *our\_reserve\_msat*: A string describing the minimum amount that + you must keep in the channel when you attempt to send out; + if you have less than this in the channel, you cannot send out + via this channel; + a number followed by a string unit. + The peer imposes this on us, default is 1% of the total channel capacity. +* *spendable\_msat* and *receivable\_msat*: A string describing an + ***estimate*** of how much we can send or receive over this channel in a + single payment (or payment-part for multi-part payments); + a number followed by a string unit. + This is an ***estimate***, which can be wrong because adding HTLCs requires + an increase in fees paid to onchain miners, and onchain fees change + dynamically according to onchain activity. + For a sufficiently-large channel, this can be limited by the rules imposed + under certain blockchains; + for example, individual Bitcoin mainnet payment-parts cannot exceed + 42.94967295 mBTC. +* *minimum\_htlc\_in\_msat*: A string describing the minimum amount that + an HTLC must have before we accept it. +* *their\_to\_self\_delay*: The number of blocks that the peer must wait + to claim their funds, if they close unilaterally. +* *our\_to\_self\_delay*: The number of blocks that you must wait to claim + your funds, if you close unilaterally. +* *max\_accepted\_htlcs*: The maximum number of HTLCs you will accept on + this channel. +* *in\_payments\_offered*: The number of incoming HTLCs offered over this + channel. +* *in\_offered\_msat*: A string describing the total amount of all incoming + HTLCs offered over this channel; + a number followed by a string unit. +* *in\_payments\_fulfilled*: The number of incoming HTLCs offered *and + successfully claimed* over this channel. +* *in\_fulfilled\_msat*: A string describing the total amount of all + incoming HTLCs offered *and successfully claimed* over this channel; + a number followed by a string unit. +* *out\_payments\_offered*: The number of outgoing HTLCs offered over + this channel. +* *out\_offered\_msat*: A string describing the total amount of all + outgoing HTLCs offered over this channel; + a number followed by a string unit. +* *out\_payments\_fulfilled*: The number of outgoing HTLCs offered *and + successfully claimed* over this channel. +* *out\_fulfilled\_msat*: A string describing the total amount of all + outgoing HTLCs offered *and successfully claimed* over this channel; + a number followed by a string unit. +* *scratch\_txid*: The txid of the latest transaction (what we would sign and + send to chain if the channel were to fail now). +* *last\_tx\_fee*: The fee on that latest transaction. +* *feerate*: An object containing the latest feerate as both *perkw* and *perkb*. +* *htlcs*: An array of objects describing the HTLCs currently in-flight + in the channel. + +Objects in the *htlcs* array will contain these fields: + +* *direction*: Either the string `"out"` or `"in"`, whether it is an + outgoing or incoming HTLC. +* *id*: A numeric ID uniquely identifying this HTLC. +* *amount\_msat*: The value of the HTLC. +* *expiry*: The blockheight at which the HTLC will be forced to return + to its offerer: an `"in"` HTLC will be returned to the peer, an + `"out"` HTLC will be returned to you. + **NOTE** If the *expiry* of any outgoing HTLC will arrive in the next + block, `lightningd`(8) will automatically unilaterally close the + channel in order to enforce the timeout onchain. +* *payment\_hash*: The payment hash, whose preimage must be revealed to + successfully claim this HTLC. +* *state*: A string describing whether the HTLC has been communicated to + or from the peer, whether it has been signed in a new commitment, whether + the previous commitment (that does not contain it) has been revoked, as + well as when the HTLC is fulfilled or failed offchain. +* *local\_trimmed*: A boolean, existing and `true` if the HTLC is not + actually instantiated as an output (i.e. "trimmed") on the commitment + transaction (and will not be instantiated on a unilateral close). + Generally true if the HTLC is below the *dust\_limit\_msat* for the + channel. + +On error the returned object will contain `code` and `message` properties, +with `code` being one of the following: + +- -32602: If the given parameters are wrong. + +AUTHOR +------ + +Michael Hawkins <>. + +SEE ALSO +-------- + +lightning-connect(7), lightning-fundchannel\_start(7), +lightning-setchannelfee(7) + +RESOURCES +--------- + +Main web site: Lightning +RFC site (BOLT \#9): + + +[comment]: # ( SHA256STAMP:adc1f36b764f1d98ba6a34b63f459a19db15fc94e37678806a1eb858a2166167) diff --git a/doc/schemas/listpeerchannels.request.json b/doc/schemas/listpeerchannels.request.json new file mode 100644 index 000000000000..05950858ca4f --- /dev/null +++ b/doc/schemas/listpeerchannels.request.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": { + "id": { + "type": "pubkey", + "description": "If supplied, limits the channels to just the peer with the given ID, if it exists." + } + } +} diff --git a/doc/schemas/listpeerchannels.schema.json b/doc/schemas/listpeerchannels.schema.json new file mode 100644 index 000000000000..3f5d21d98a5e --- /dev/null +++ b/doc/schemas/listpeerchannels.schema.json @@ -0,0 +1,980 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channels" + ], + "properties": { + "channels": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "state", + "opener", + "features", + "peer_connected", + "peer_id" + ], + "properties": { + "peer_id": { + "type": "pubkey", + "description": "Node Public key" + }, + "peer_connected": { + "type": "boolean", + "description": "A boolean flag that is set to true if the peer is online" + }, + "state": { + "type": "string", + "enum": [ + "OPENINGD", + "CHANNELD_AWAITING_LOCKIN", + "CHANNELD_NORMAL", + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN", + "DUALOPEND_OPEN_INIT", + "DUALOPEND_AWAITING_LOCKIN" + ], + "description": "the channel state, in particular \"CHANNELD_NORMAL\" means the channel can be used normally" + }, + "scratch_txid": { + "type": "txid", + "description": "The txid we would use if we went onchain now" + }, + "feerate": { + "type": "object", + "description": "Feerates for the current tx", + "additionalProperties": false, + "required": [ + "perkw", + "perkb" + ], + "properties": { + "perkw": { + "type": "u32", + "description": "Feerate per 1000 weight (i.e kSipa)" + }, + "perkb": { + "type": "u32", + "description": "Feerate per 1000 virtual bytes" + } + } + }, + "owner": { + "type": "string", + "description": "The current subdaemon controlling this connection" + }, + "short_channel_id": { + "type": "short_channel_id", + "description": "The short_channel_id (once locked in)" + }, + "channel_id": { + "type": "hash", + "description": "The full channel_id", + "minLength": 64, + "maxLength": 64 + }, + "funding_txid": { + "type": "txid", + "description": "ID of the funding transaction" + }, + "funding_outnum": { + "type": "u32", + "description": "The 0-based output number of the funding transaction which opens the channel" + }, + "initial_feerate": { + "type": "string", + "description": "For inflight opens, the first feerate used to initiate the channel open" + }, + "last_feerate": { + "type": "string", + "description": "For inflight opens, the most recent feerate used on the channel open" + }, + "next_feerate": { + "type": "string", + "description": "For inflight opens, the next feerate we'll use for the channel open" + }, + "next_fee_step": { + "type": "u32", + "description": "For inflight opens, the next feerate step we'll use for the channel open" + }, + "inflight": { + "type": "array", + "description": "Current candidate funding transactions (only for dual-funding)", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "funding_txid", + "funding_outnum", + "feerate", + "total_funding_msat", + "our_funding_msat", + "scratch_txid" + ], + "properties": { + "funding_txid": { + "type": "txid", + "description": "ID of the funding transaction" + }, + "funding_outnum": { + "type": "u32", + "description": "The 0-based output number of the funding transaction which opens the channel" + }, + "feerate": { + "type": "string", + "description": "The feerate for this funding transaction in per-1000-weight, with \"kpw\" appended" + }, + "total_funding_msat": { + "type": "msat", + "description": "total amount in the channel" + }, + "our_funding_msat": { + "type": "msat", + "description": "amount we have in the channel" + }, + "scratch_txid": { + "type": "txid", + "description": "The commitment transaction txid we would use if we went onchain now" + } + } + } + }, + "close_to": { + "type": "hex", + "description": "scriptPubkey which we have to close to if we mutual close" + }, + "private": { + "type": "boolean", + "description": "if False, we will not announce this channel" + }, + "opener": { + "type": "string", + "enum": [ + "local", + "remote" + ], + "description": "Who initiated the channel" + }, + "closer": { + "type": "string", + "enum": [ + "local", + "remote" + ], + "description": "Who initiated the channel close" + }, + "features": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "option_static_remotekey", + "option_anchor_outputs", + "option_zeroconf" + ], + "description": "BOLT #9 features which apply to this channel" + } + }, + "funding": { + "type": "object", + "additionalProperties": false, + "required": [ + "local_funds_msat", + "remote_funds_msat" + ], + "properties": { + "local_msat": { + "type": "msat", + "description": "Amount of channel we funded (deprecated)" + }, + "remote_msat": { + "type": "msat", + "description": "Amount of channel they funded (deprecated)" + }, + "pushed_msat": { + "type": "msat", + "description": "Amount pushed from opener to peer" + }, + "local_funds_msat": { + "type": "msat", + "description": "Amount of channel we funded" + }, + "remote_funds_msat": { + "type": "msat", + "description": "Amount of channel they funded" + }, + "fee_paid_msat": { + "type": "msat", + "description": "Amount we paid peer at open" + }, + "fee_rcvd_msat": { + "type": "msat", + "description": "Amount we were paid by peer at open" + } + } + }, + "to_us_msat": { + "type": "msat", + "description": "how much of channel is owed to us" + }, + "min_to_us_msat": { + "type": "msat", + "description": "least amount owed to us ever" + }, + "max_to_us_msat": { + "type": "msat", + "description": "most amount owed to us ever" + }, + "total_msat": { + "type": "msat", + "description": "total amount in the channel" + }, + "fee_base_msat": { + "type": "msat", + "description": "amount we charge to use the channel" + }, + "fee_proportional_millionths": { + "type": "u32", + "description": "amount we charge to use the channel in parts-per-million" + }, + "dust_limit_msat": { + "type": "msat", + "description": "minimum amount for an output on the channel transactions" + }, + "max_total_htlc_in_msat": { + "type": "msat", + "description": "max amount accept in a single payment" + }, + "their_reserve_msat": { + "type": "msat", + "description": "minimum we insist they keep in channel" + }, + "our_reserve_msat": { + "type": "msat", + "description": "minimum they insist we keep in channel" + }, + "spendable_msat": { + "type": "msat", + "description": "total we could send through channel" + }, + "receivable_msat": { + "type": "msat", + "description": "total peer could send through channel" + }, + "minimum_htlc_in_msat": { + "type": "msat", + "description": "the minimum amount HTLC we accept" + }, + "minimum_htlc_out_msat": { + "type": "msat", + "description": "the minimum amount HTLC we will send" + }, + "maximum_htlc_out_msat": { + "type": "msat", + "description": "the maximum amount HTLC we will send" + }, + "their_to_self_delay": { + "type": "u32", + "description": "the number of blocks before they can take their funds if they unilateral close" + }, + "our_to_self_delay": { + "type": "u32", + "description": "the number of blocks before we can take our funds if we unilateral close" + }, + "max_accepted_htlcs": { + "type": "u32", + "description": "Maximum number of incoming HTLC we will accept at once" + }, + "msatoshi_to_us": { + "deprecated": true + }, + "msatoshi_to_us_min": { + "deprecated": true + }, + "msatoshi_to_us_max": { + "deprecated": true + }, + "msatoshi_total": { + "deprecated": true + }, + "dust_limit_satoshis": { + "deprecated": true + }, + "max_htlc_value_in_flight_msat": { + "deprecated": true + }, + "our_channel_reserve_satoshis": { + "deprecated": true + }, + "their_channel_reserve_satoshis": { + "deprecated": true + }, + "spendable_msatoshi": { + "deprecated": true + }, + "receivable_msatoshi": { + "deprecated": true + }, + "htlc_minimum_msat": { + "deprecated": true + }, + "alias": { + "type": "object", + "required": [], + "properties": { + "local": { + "type": "short_channel_id", + "description": "An alias assigned by this node to this channel, used for outgoing payments" + }, + "remote": { + "type": "short_channel_id", + "description": "An alias assigned by the remote node to this channel, usable in routehints and invoices" + } + } + }, + "state_changes": { + "type": "array", + "description": "Prior state changes", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "timestamp", + "old_state", + "new_state", + "cause", + "message" + ], + "properties": { + "timestamp": { + "type": "string", + "description": "UTC timestamp of form YYYY-mm-ddTHH:MM:SS.%03dZ" + }, + "old_state": { + "type": "string", + "enum": [ + "OPENINGD", + "CHANNELD_AWAITING_LOCKIN", + "CHANNELD_NORMAL", + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN", + "DUALOPEND_OPEN_INIT", + "DUALOPEND_AWAITING_LOCKIN" + ], + "description": "Previous state" + }, + "new_state": { + "type": "string", + "enum": [ + "OPENINGD", + "CHANNELD_AWAITING_LOCKIN", + "CHANNELD_NORMAL", + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN", + "DUALOPEND_OPEN_INIT", + "DUALOPEND_AWAITING_LOCKIN" + ], + "description": "New state" + }, + "cause": { + "type": "string", + "enum": [ + "unknown", + "local", + "user", + "remote", + "protocol", + "onchain" + ], + "description": "What caused the change" + }, + "message": { + "type": "string", + "description": "Human-readable explanation" + } + } + } + }, + "status": { + "type": "array", + "items": { + "type": "string", + "description": "Billboard log of significant changes" + } + }, + "in_payments_offered": { + "type": "u64", + "description": "Number of incoming payment attempts" + }, + "in_offered_msat": { + "type": "msat", + "description": "Total amount of incoming payment attempts" + }, + "in_msatoshi_offered": { + "deprecated": true + }, + "in_payments_fulfilled": { + "type": "u64", + "description": "Number of successful incoming payment attempts" + }, + "in_fulfilled_msat": { + "type": "msat", + "description": "Total amount of successful incoming payment attempts" + }, + "in_msatoshi_fulfilled": { + "deprecated": true + }, + "out_payments_offered": { + "type": "u64", + "description": "Number of outgoing payment attempts" + }, + "out_offered_msat": { + "type": "msat", + "description": "Total amount of outgoing payment attempts" + }, + "out_msatoshi_offered": { + "deprecated": true + }, + "out_payments_fulfilled": { + "type": "u64", + "description": "Number of successful outgoing payment attempts" + }, + "out_fulfilled_msat": { + "type": "msat", + "description": "Total amount of successful outgoing payment attempts" + }, + "out_msatoshi_fulfilled": { + "deprecated": true + }, + "htlcs": { + "type": "array", + "description": "current HTLCs in this channel", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "direction", + "id", + "amount_msat", + "expiry", + "payment_hash", + "state" + ], + "properties": { + "direction": { + "type": "string", + "enum": [ + "in", + "out" + ], + "description": "Whether it came from peer, or is going to peer" + }, + "id": { + "type": "u64", + "description": "Unique ID for this htlc on this channel in this direction" + }, + "amount_msat": { + "type": "msat", + "description": "Amount send/received for this HTLC" + }, + "msatoshi": { + "deprecated": true + }, + "expiry": { + "type": "u32", + "description": "Block this HTLC expires at" + }, + "payment_hash": { + "type": "hash", + "description": "the hash of the payment_preimage which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "local_trimmed": { + "type": "boolean", + "enum": [ + true + ], + "description": "if this is too small to enforce onchain" + }, + "status": { + "type": "string", + "description": "set if this HTLC is currently waiting on a hook (and shows what plugin)" + } + }, + "allOf": [ + { + "if": { + "properties": { + "direction": { + "enum": [ + "out" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "state" + ], + "properties": { + "direction": {}, + "id": {}, + "amount_msat": {}, + "msatoshi": {}, + "expiry": {}, + "payment_hash": {}, + "local_trimmed": {}, + "status": {}, + "alias": {}, + "peer_id": {}, + "peer_connected": {}, + "state": { + "type": "string", + "enum": [ + "SENT_ADD_HTLC", + "SENT_ADD_COMMIT", + "RCVD_ADD_REVOCATION", + "RCVD_ADD_ACK_COMMIT", + "SENT_ADD_ACK_REVOCATION", + "RCVD_REMOVE_HTLC", + "RCVD_REMOVE_COMMIT", + "SENT_REMOVE_REVOCATION", + "SENT_REMOVE_ACK_COMMIT", + "RCVD_REMOVE_ACK_REVOCATION" + ], + "description": "Status of the HTLC" + } + } + } + }, + { + "if": { + "properties": { + "direction": { + "enum": [ + "in" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "state" + ], + "properties": { + "direction": {}, + "id": {}, + "amount_msat": {}, + "msatoshi": {}, + "expiry": {}, + "payment_hash": {}, + "local_trimmed": {}, + "status": {}, + "peer_id": {}, + "peer_connected": {}, + "state": { + "type": "string", + "enum": [ + "RCVD_ADD_HTLC", + "RCVD_ADD_COMMIT", + "SENT_ADD_REVOCATION", + "SENT_ADD_ACK_COMMIT", + "RCVD_ADD_ACK_REVOCATION", + "SENT_REMOVE_HTLC", + "SENT_REMOVE_COMMIT", + "RCVD_REMOVE_REVOCATION", + "RCVD_REMOVE_ACK_COMMIT", + "SENT_REMOVE_ACK_REVOCATION" + ], + "description": "Status of the HTLC" + } + } + } + } + ] + } + } + }, + "allOf": [ + { + "if": { + "required": [ + "close_to" + ] + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "state": {}, + "peer_id": {}, + "peer_connected": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "funding_outnum": {}, + "close_to": {}, + "private": {}, + "alias": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, + "maximum_htlc_out_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "initial_feerate": {}, + "last_feerate": {}, + "next_feerate": {}, + "inflight": {}, + "last_tx_fee_msat": {}, + "direction": {}, + "close_to_addr": { + "type": "string", + "description": "The bitcoin address we will close to" + } + } + } + }, + { + "if": { + "required": [ + "scratch_txid" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "last_tx_fee_msat" + ], + "properties": { + "state": {}, + "peer_id": {}, + "peer_connected": {}, + "alias": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "funding_outnum": {}, + "inflight": {}, + "close_to": {}, + "private": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, + "maximum_htlc_out_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "initial_feerate": {}, + "last_feerate": {}, + "next_feerate": {}, + "close_to_addr": {}, + "direction": {}, + "last_tx_fee_msat": { + "type": "msat", + "description": "fee attached to this the current tx" + } + } + } + }, + { + "if": { + "required": [ + "short_channel_id" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "direction" + ], + "properties": { + "alias": {}, + "peer_id": {}, + "peer_connected": {}, + "state": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "funding_outnum": {}, + "inflight": {}, + "close_to": {}, + "private": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, + "maximum_htlc_out_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "initial_feerate": {}, + "last_feerate": {}, + "next_feerate": {}, + "close_to_addr": {}, + "last_tx_fee_msat": {}, + "direction": { + "type": "u32", + "description": "0 if we're the lesser node_id, 1 if we're the greater" + } + } + } + }, + { + "if": { + "required": [ + "inflight" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "initial_feerate", + "last_feerate", + "next_feerate" + ], + "properties": { + "state": {}, + "peer_id": {}, + "peer_connected": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "alias": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "funding_outnum": {}, + "close_to": {}, + "private": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, + "maximum_htlc_out_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "inflight": {}, + "close_to_addr": {}, + "direction": {}, + "last_tx_fee_msat": {}, + "initial_feerate": { + "type": "string", + "description": "The feerate for the initial funding transaction in per-1000-weight, with \"kpw\" appended" + }, + "last_feerate": { + "type": "string", + "description": "The feerate for the latest funding transaction in per-1000-weight, with \"kpw\" appended" + }, + "next_feerate": { + "type": "string", + "description": "The minimum feerate for the next funding transaction in per-1000-weight, with \"kpw\" appended" + } + } + } + } + ] + } + } + } +} diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 7b18d2cc1609..437469d6a5fd 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2047,6 +2047,60 @@ static const struct json_command staticbackup_command = { /* Comment added to satisfice AUTODATA */ AUTODATA(json_command, &staticbackup_command); +static void json_add_peerchannels(struct lightningd *ld, + struct json_stream *response, + const struct peer *peer) +{ + struct channel *channel; + + json_add_uncommitted_channel(response, peer->uncommitted_channel, peer); + list_for_each(&peer->channels, channel, list) { + if (channel_unsaved(channel)) + json_add_unsaved_channel(response, channel, peer); + else + json_add_channel(ld, response, NULL, channel, peer); + } +} + +static struct command_result *json_listpeerchannels(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct node_id *peer_id; + struct peer *peer; + struct json_stream *response; + + /* FIME: filter by status */ + if (!param(cmd, buffer, params, + p_opt("id", param_node_id, &peer_id), + NULL)) + return command_param_failed(); + + response = json_stream_success(cmd); + json_array_start(response, "channels"); + + if (peer_id) { + peer = peer_by_id(cmd->ld, peer_id); + if (peer) + json_add_peerchannels(cmd->ld, response, peer); + } else { + list_for_each(&cmd->ld->peers, peer, list) + json_add_peerchannels(cmd->ld, response, peer); + } + + json_array_end(response); + + return command_success(cmd, response); +} + +static const struct json_command listpeerchannels_command = { + "listpeerchannels", + "network", + json_listpeerchannels, + "Show channels with direct peers." +}; +AUTODATA(json_command, &listpeerchannels_command); struct command_result * command_find_channel(struct command *cmd, From de9ecd19bee85efa80801bc92928ff352d639d3c Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 12 Jan 2023 11:47:10 +1030 Subject: [PATCH 03/14] plugins: make bookkeeper use the new listpeerchannels command. --- plugins/bkpr/bookkeeper.c | 244 ++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 130 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index e541d00674f4..da8d7a3b0ea2 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -612,138 +612,123 @@ static bool new_missed_channel_account(struct command *cmd, u64 timestamp) { struct chain_event *chain_ev; - size_t i, j; - const jsmntok_t *curr_peer, *curr_chan, - *peer_arr_tok, *chan_arr_tok; - - peer_arr_tok = json_get_member(buf, result, "peers"); - assert(peer_arr_tok->type == JSMN_ARRAY); - /* There should only be one peer */ - json_for_each_arr(i, curr_peer, peer_arr_tok) { - const char *err; - struct node_id peer_id; + const char *err; + size_t i; + const jsmntok_t *curr_chan, *chan_arr_tok; - err = json_scan(cmd, buf, curr_peer, "{id:%}", - JSON_SCAN(json_to_node_id, &peer_id)); + chan_arr_tok = json_get_member(buf, result, "channels"); + assert(chan_arr_tok && chan_arr_tok->type == JSMN_ARRAY); + json_for_each_arr(i, curr_chan, chan_arr_tok) { + struct bitcoin_outpoint opt; + struct amount_msat amt, remote_amt, + push_credit, push_debit; + struct node_id peer_id; + char *opener, *chan_id; + enum mvt_tag *tags; + bool ok, is_opener, is_leased; + + err = json_scan(tmpctx, buf, curr_chan, + "{peer_id:%," + "channel_id:%," + "funding_txid:%," + "funding_outnum:%," + "funding:{local_funds_msat:%," + "remote_funds_msat:%}," + "opener:%}", + JSON_SCAN(json_to_node_id, &peer_id), + JSON_SCAN_TAL(tmpctx, json_strdup, &chan_id), + JSON_SCAN(json_to_txid, &opt.txid), + JSON_SCAN(json_to_number, &opt.n), + JSON_SCAN(json_to_msat, &amt), + JSON_SCAN(json_to_msat, &remote_amt), + JSON_SCAN_TAL(tmpctx, json_strdup, &opener)); if (err) plugin_err(cmd->plugin, - "failure scanning listpeer" + "failure scanning listpeerchannels" " result: %s", err); - json_get_member(buf, curr_peer, "id"); - chan_arr_tok = json_get_member(buf, curr_peer, - "channels"); - assert(chan_arr_tok->type == JSMN_ARRAY); - json_for_each_arr(j, curr_chan, chan_arr_tok) { - struct bitcoin_outpoint opt; - struct amount_msat amt, remote_amt, - push_credit, push_debit; - char *opener, *chan_id; - enum mvt_tag *tags; - bool ok, is_opener, is_leased; - - err = json_scan(tmpctx, buf, curr_chan, - "{channel_id:%," - "funding_txid:%," - "funding_outnum:%," - "funding:{local_funds_msat:%," - "remote_funds_msat:%}," - "opener:%}", - JSON_SCAN_TAL(tmpctx, json_strdup, &chan_id), - JSON_SCAN(json_to_txid, &opt.txid), - JSON_SCAN(json_to_number, &opt.n), - JSON_SCAN(json_to_msat, &amt), - JSON_SCAN(json_to_msat, &remote_amt), - JSON_SCAN_TAL(tmpctx, json_strdup, &opener)); - if (err) - plugin_err(cmd->plugin, - "failure scanning listpeer" - " result: %s", err); + if (!streq(chan_id, acct->name)) + continue; - if (!streq(chan_id, acct->name)) - continue; + plugin_log(cmd->plugin, LOG_DBG, + "Logging channel account from list %s", + acct->name); - plugin_log(cmd->plugin, LOG_DBG, - "Logging channel account from list %s", - acct->name); - - chain_ev = tal(cmd, struct chain_event); - chain_ev->tag = mvt_tag_str(CHANNEL_OPEN); - chain_ev->debit = AMOUNT_MSAT(0); - ok = amount_msat_add(&chain_ev->output_value, - amt, remote_amt); - assert(ok); - chain_ev->currency = tal_strdup(chain_ev, currency); - chain_ev->origin_acct = NULL; - /* 2s before the channel opened, minimum */ - chain_ev->timestamp = timestamp - 2; - chain_ev->blockheight = 0; - chain_ev->outpoint = opt; - chain_ev->spending_txid = NULL; - chain_ev->payment_id = NULL; - chain_ev->ignored = false; - chain_ev->stealable = false; - chain_ev->desc = NULL; - - /* Update the account info too */ - tags = tal_arr(chain_ev, enum mvt_tag, 1); - tags[0] = CHANNEL_OPEN; - - is_opener = streq(opener, "local"); - - /* Leased/pushed channels have some extra work */ - find_push_amts(buf, curr_chan, is_opener, - &push_credit, &push_debit, - &is_leased); - - if (is_leased) - tal_arr_expand(&tags, LEASED); - if (is_opener) - tal_arr_expand(&tags, OPENER); - - chain_ev->credit = amt; - db_begin_transaction(db); - if (!log_chain_event(db, acct, chain_ev)) - goto done; - - maybe_update_account(db, acct, chain_ev, - tags, 0, &peer_id); - maybe_update_onchain_fees(cmd, db, &opt.txid); - - /* We won't count the close's fees if we're - * *not* the opener, which we didn't know - * until now, so now try to update the - * fees for the close tx's spending_txid..*/ - if (acct->closed_event_db_id) - try_update_open_fees(cmd, acct); - - /* We log a channel event for the push amt */ - if (!amount_msat_zero(push_credit) - || !amount_msat_zero(push_debit)) { - struct channel_event *chan_ev; - char *chan_tag; - - chan_tag = tal_fmt(tmpctx, "%s", - mvt_tag_str( - is_leased ? - LEASE_FEE : PUSHED)); - - chan_ev = new_channel_event(tmpctx, - chan_tag, - push_credit, - push_debit, - AMOUNT_MSAT(0), - currency, - NULL, 0, - timestamp - 1); - log_channel_event(db, acct, chan_ev); - } + chain_ev = tal(cmd, struct chain_event); + chain_ev->tag = mvt_tag_str(CHANNEL_OPEN); + chain_ev->debit = AMOUNT_MSAT(0); + ok = amount_msat_add(&chain_ev->output_value, + amt, remote_amt); + assert(ok); + chain_ev->currency = tal_strdup(chain_ev, currency); + chain_ev->origin_acct = NULL; + /* 2s before the channel opened, minimum */ + chain_ev->timestamp = timestamp - 2; + chain_ev->blockheight = 0; + chain_ev->outpoint = opt; + chain_ev->spending_txid = NULL; + chain_ev->payment_id = NULL; + chain_ev->ignored = false; + chain_ev->stealable = false; + chain_ev->desc = NULL; + + /* Update the account info too */ + tags = tal_arr(chain_ev, enum mvt_tag, 1); + tags[0] = CHANNEL_OPEN; + + is_opener = streq(opener, "local"); + + /* Leased/pushed channels have some extra work */ + find_push_amts(buf, curr_chan, is_opener, + &push_credit, &push_debit, + &is_leased); + + if (is_leased) + tal_arr_expand(&tags, LEASED); + if (is_opener) + tal_arr_expand(&tags, OPENER); + + chain_ev->credit = amt; + db_begin_transaction(db); + if (!log_chain_event(db, acct, chain_ev)) + goto done; + + maybe_update_account(db, acct, chain_ev, + tags, 0, &peer_id); + maybe_update_onchain_fees(cmd, db, &opt.txid); + + /* We won't count the close's fees if we're + * *not* the opener, which we didn't know + * until now, so now try to update the + * fees for the close tx's spending_txid..*/ + if (acct->closed_event_db_id) + try_update_open_fees(cmd, acct); + + /* We log a channel event for the push amt */ + if (!amount_msat_zero(push_credit) + || !amount_msat_zero(push_debit)) { + struct channel_event *chan_ev; + char *chan_tag; + + chan_tag = tal_fmt(tmpctx, "%s", + mvt_tag_str( + is_leased ? + LEASE_FEE : PUSHED)); + chan_ev = new_channel_event(tmpctx, + chan_tag, + push_credit, + push_debit, + AMOUNT_MSAT(0), + currency, + NULL, 0, + timestamp - 1); + log_channel_event(db, acct, chan_ev); + } done: db_commit_transaction(db); return true; - } } return false; @@ -864,8 +849,7 @@ static struct command_result *log_error(struct command *cmd, return notification_handled(cmd); } -static struct command_result * -listpeers_multi_done(struct command *cmd, +static struct command_result *listpeerchannels_multi_done(struct command *cmd, const char *buf, const jsmntok_t *result, struct new_account_info **new_accts) @@ -882,7 +866,7 @@ listpeers_multi_done(struct command *cmd, info->currency, info->timestamp)) { plugin_log(cmd->plugin, LOG_BROKEN, - "Unable to find account %s in listpeers", + "Unable to find account %s in listpeerchannels", info->acct->name); continue; } @@ -916,7 +900,6 @@ listpeers_multi_done(struct command *cmd, info->timestamp - 1, credit_diff, debit_diff); } - plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); return notification_handled(cmd); } @@ -1131,10 +1114,11 @@ static struct command_result *json_balance_snapshot(struct command *cmd, struct out_req *req; req = jsonrpc_request_start(cmd->plugin, cmd, - "listpeers", - listpeers_multi_done, + "listpeerchannels", + listpeerchannels_multi_done, log_error, new_accts); + /* FIXME(vicenzopalazzo) require the channel by channel_id to avoid parsing not useful json */ return send_outreq(cmd->plugin, req); } @@ -1318,7 +1302,7 @@ struct event_info { }; static struct command_result * -listpeers_done(struct command *cmd, const char *buf, +listpeerchannels_done(struct command *cmd, const char *buf, const jsmntok_t *result, struct event_info *info) { struct acct_balance **balances, *bal; @@ -1560,7 +1544,7 @@ parse_and_log_chain_move(struct command *cmd, plugin_log(cmd->plugin, LOG_DBG, "channel event received but no open for channel %s." - " Calling `listpeers` to fetch missing info", + " Calling `listpeerchannls` to fetch missing info", acct->name); info = tal(cmd, struct event_info); @@ -1570,8 +1554,8 @@ parse_and_log_chain_move(struct command *cmd, acct : orig_acct); req = jsonrpc_request_start(cmd->plugin, cmd, - "listpeers", - listpeers_done, + "listpeerchannels", + listpeerchannels_done, log_error, info); /* FIXME: use the peer_id to reduce work here */ From 21387763f5cc79962d3e3e0a9e9d57d117264714 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:48:10 +1030 Subject: [PATCH 04/14] plugins/libplugin: flatten return from json_to_listpeers_result. Instead of returning a peers -> channels heirarchy, return (as callers want!) a flat array of channels. This is actually most of the transition work to make them work with listpeerchannels. Signed-off-by: Rusty Russell --- plugins/libplugin-pay.c | 45 +++++++++--------- plugins/libplugin.c | 78 +++++++++---------------------- plugins/libplugin.h | 21 +++------ plugins/test/run-route-overlong.c | 10 ++-- 4 files changed, 53 insertions(+), 101 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 30ac1f700c7e..d47430aab663 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3284,34 +3284,31 @@ static struct command_result *direct_pay_listpeers(struct command *cmd, const jsmntok_t *toks, struct payment *p) { - struct listpeers_result *r = - json_to_listpeers_result(tmpctx, buffer, toks); + struct listpeers_channel **channels = json_to_listpeers_channels(tmpctx, buffer, toks); struct direct_pay_data *d = payment_mod_directpay_get_data(p); - if (r && tal_count(r->peers) == 1) { - struct listpeers_peer *peer = r->peers[0]; - if (!peer->connected) - goto cont; - - for (size_t i=0; ichannels); i++) { - struct listpeers_channel *chan = r->peers[0]->channels[i]; - if (!streq(chan->state, "CHANNELD_NORMAL")) - continue; - - /* Must have either a local alias for zeroconf - * channels or a final scid. */ - assert(chan->alias[LOCAL] || chan->scid); - d->chan = tal(d, struct short_channel_id_dir); - if (chan->scid) { - d->chan->scid = *chan->scid; - d->chan->dir = *chan->direction; - } else { - d->chan->scid = *chan->alias[LOCAL]; - d->chan->dir = 0; /* Don't care. */ - } + for (size_t i=0; iconnected) + continue; + + if (!streq(chan->state, "CHANNELD_NORMAL")) + continue; + + /* Must have either a local alias for zeroconf + * channels or a final scid. */ + assert(chan->alias[LOCAL] || chan->scid); + d->chan = tal(d, struct short_channel_id_dir); + if (chan->scid) { + d->chan->scid = *chan->scid; + d->chan->dir = *chan->direction; + } else { + d->chan->scid = *chan->alias[LOCAL]; + d->chan->dir = 0; /* Don't care. */ } } -cont: + direct_pay_override(p); return command_still_pending(cmd); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index ed9bd46c2458..19d9b3f2515d 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1914,15 +1914,6 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, json_get_member(buffer, tok, "spendable_msat"), *aliastok = json_get_member(buffer, tok, "alias"); - if (privtok == NULL || privtok->type != JSMN_PRIMITIVE || - statetok == NULL || statetok->type != JSMN_STRING || - ftxidtok == NULL || ftxidtok->type != JSMN_STRING || - (scidtok != NULL && scidtok->type != JSMN_STRING) || - (dirtok != NULL && dirtok->type != JSMN_PRIMITIVE) || - tmsattok == NULL || - smsattok == NULL) - return NULL; - chan = tal(ctx, struct listpeers_channel); json_to_bool(buffer, privtok, &chan->private); @@ -1973,70 +1964,43 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, return chan; } -static struct listpeers_peer *json_to_listpeers_peer(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok) +/* Append channels for this peer */ +static void json_add_listpeers_peer(struct listpeers_channel ***chans, + const char *buffer, + const jsmntok_t *tok) { - struct listpeers_peer *res; size_t i; const jsmntok_t *iter; const jsmntok_t *idtok = json_get_member(buffer, tok, "id"), *conntok = json_get_member(buffer, tok, "connected"), - *netaddrtok = json_get_member(buffer, tok, "netaddr"), *channelstok = json_get_member(buffer, tok, "channels"); + bool connected; + struct node_id id; - /* Preliminary sanity checks. */ - if (idtok == NULL || idtok->type != JSMN_STRING || conntok == NULL || - conntok->type != JSMN_PRIMITIVE || - (netaddrtok != NULL && netaddrtok->type != JSMN_ARRAY) || - channelstok == NULL || channelstok->type != JSMN_ARRAY) - return NULL; - - res = tal(ctx, struct listpeers_peer); - json_to_node_id(buffer, idtok, &res->id); - json_to_bool(buffer, conntok, &res->connected); + json_to_node_id(buffer, idtok, &id); + json_to_bool(buffer, conntok, &connected); - res->netaddr = tal_arr(res, const char *, 0); - if (netaddrtok != NULL) { - json_for_each_arr(i, iter, netaddrtok) { - tal_arr_expand(&res->netaddr, - json_strdup(res, buffer, iter)); - } - } - - res->channels = tal_arr(res, struct listpeers_channel *, 0); json_for_each_arr(i, iter, channelstok) { - struct listpeers_channel *chan = json_to_listpeers_channel(res, buffer, iter); - assert(chan != NULL); - tal_arr_expand(&res->channels, chan); + struct listpeers_channel *chan = json_to_listpeers_channel(*chans, buffer, iter); + chan->id = id; + chan->connected = connected; + tal_arr_expand(chans, chan); } - - return res; } -struct listpeers_result *json_to_listpeers_result(const tal_t *ctx, - const char *buffer, - const jsmntok_t *toks) +struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok) { size_t i; const jsmntok_t *iter; - struct listpeers_result *res; - const jsmntok_t *peerstok = json_get_member(buffer, toks, "peers"); - - if (peerstok == NULL || peerstok->type != JSMN_ARRAY) - return NULL; - - res = tal(ctx, struct listpeers_result); - res->peers = tal_arr(res, struct listpeers_peer *, 0); + const jsmntok_t *peerstok = json_get_member(buffer, tok, "peers"); + struct listpeers_channel **chans; - json_for_each_obj(i, iter, peerstok) { - struct listpeers_peer *p = - json_to_listpeers_peer(res, buffer, iter); - if (p == NULL) - return tal_free(res); - tal_arr_expand(&res->peers, p); - } - return res; + chans = tal_arr(ctx, struct listpeers_channel *, 0); + json_for_each_obj(i, iter, peerstok) + json_add_listpeers_peer(&chans, buffer, iter); + return chans; } struct createonion_response *json_to_createonion_response(const tal_t *ctx, diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 57fd44609305..bd74f5827a9f 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -433,6 +433,8 @@ void NORETURN LAST_ARG_NULL plugin_main(char *argv[], ...); struct listpeers_channel { + struct node_id id; + bool connected; bool private; struct bitcoin_txid funding_txid; const char *state; @@ -444,21 +446,10 @@ struct listpeers_channel { /* TODO Add fields as we need them. */ }; -struct listpeers_peer { - struct node_id id; - bool connected; - const char **netaddr; - struct feature_set *features; - struct listpeers_channel **channels; -}; - -struct listpeers_result { - struct listpeers_peer **peers; -}; - -struct listpeers_result *json_to_listpeers_result(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok); +/* Returns an array of listpeers_channel * */ +struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok); struct createonion_response { u8 *onion; diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index a20778629a36..a22863508b37 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -133,11 +133,11 @@ struct createonion_response *json_to_createonion_response(const tal_t *ctx UNNEE /* Generated stub for json_to_int */ bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED) { fprintf(stderr, "json_to_int called!\n"); abort(); } -/* Generated stub for json_to_listpeers_result */ -struct listpeers_result *json_to_listpeers_result(const tal_t *ctx UNNEEDED, - const char *buffer UNNEEDED, - const jsmntok_t *tok UNNEEDED) -{ fprintf(stderr, "json_to_listpeers_result called!\n"); abort(); } +/* Generated stub for json_to_listpeers_channels */ +struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_to_listpeers_channels called!\n"); abort(); } /* Generated stub for json_to_msat */ bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct amount_msat *msat UNNEEDED) From 8dd41d5baa0dd1d808a75aad2f2251faa3526a8a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:49:10 +1030 Subject: [PATCH 05/14] libplugin: don't return unopened channels from json_to_listpeers_channels(). This way we always have an SCID and a direction. Signed-off-by: Rusty Russell --- plugins/libplugin-pay.c | 3 +-- plugins/libplugin.c | 16 ++++++++-------- plugins/libplugin.h | 3 ++- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index d47430aab663..02bacca9ce3f 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3302,11 +3302,10 @@ static struct command_result *direct_pay_listpeers(struct command *cmd, d->chan = tal(d, struct short_channel_id_dir); if (chan->scid) { d->chan->scid = *chan->scid; - d->chan->dir = *chan->direction; } else { d->chan->scid = *chan->alias[LOCAL]; - d->chan->dir = 0; /* Don't care. */ } + d->chan->dir = chan->direction; } direct_pay_override(p); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 19d9b3f2515d..cebee85c67ac 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1925,14 +1925,6 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, json_to_short_channel_id(buffer, scidtok, chan->scid); } else { chan->scid = NULL; - chan->direction = NULL; - } - - if (dirtok != NULL) { - chan->direction = tal(chan, int); - json_to_int(buffer, dirtok, chan->direction); - } else { - chan->direction = NULL; } if (aliastok != NULL) { @@ -1958,6 +1950,12 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, chan->alias[REMOTE] = NULL; } + /* If we catch a channel during opening, these might not be set. + * It's not a real channel (yet), so ignore it! */ + if (!chan->scid && !chan->alias[LOCAL]) + return tal_free(chan); + + json_to_int(buffer, dirtok, &chan->direction); json_to_msat(buffer, tmsattok, &chan->total_msat); json_to_msat(buffer, smsattok, &chan->spendable_msat); @@ -1982,6 +1980,8 @@ static void json_add_listpeers_peer(struct listpeers_channel ***chans, json_for_each_arr(i, iter, channelstok) { struct listpeers_channel *chan = json_to_listpeers_channel(*chans, buffer, iter); + if (!chan) + continue; chan->id = id; chan->connected = connected; tal_arr_expand(chans, chan); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index bd74f5827a9f..a846317f1bde 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -438,9 +438,10 @@ struct listpeers_channel { bool private; struct bitcoin_txid funding_txid; const char *state; + /* scid or alias[LOCAL] is always non-NULL */ struct short_channel_id *alias[NUM_SIDES]; struct short_channel_id *scid; - int *direction; + int direction; struct amount_msat total_msat; struct amount_msat spendable_msat; /* TODO Add fields as we need them. */ From 3b7775afe17654da9e8f5fbe68ce59f89144ab64 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:50:10 +1030 Subject: [PATCH 06/14] pay: use json_to_listpeers_channels() for local_channel_hints. Don't parse the listpeers.channels output ourselves: with two extra fields we can simply reuse json_to_listpeers_channels(). Signed-off-by: Rusty Russell --- plugins/bkpr/test/run-bkpr_db.c | 4 ++ plugins/bkpr/test/run-recorder.c | 4 ++ plugins/libplugin-pay.c | 86 ++++++++++--------------------- plugins/libplugin.c | 8 ++- plugins/libplugin.h | 2 + plugins/test/run-route-overlong.c | 3 -- 6 files changed, 42 insertions(+), 65 deletions(-) diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index e71d7c1d4503..731bf4853120 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -164,6 +164,10 @@ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "json_to_txid called!\n"); abort(); } +/* Generated stub for json_to_u16 */ +bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint16_t *num UNNEEDED) +{ fprintf(stderr, "json_to_u16 called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 7872800bee16..382631ede912 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -170,6 +170,10 @@ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "json_to_txid called!\n"); abort(); } +/* Generated stub for json_to_u16 */ +bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint16_t *num UNNEEDED) +{ fprintf(stderr, "json_to_u16 called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 02bacca9ce3f..7584319bede6 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -2350,73 +2350,39 @@ static struct command_result * local_channel_hints_listpeers(struct command *cmd, const char *buffer, const jsmntok_t *toks, struct payment *p) { - const jsmntok_t *peers, *peer, *channels, *channel, *spendsats, *scid, - *dir, *connected, *max_htlc, *htlcs, *state, *alias, *alias_local; - size_t i, j; - peers = json_get_member(buffer, toks, "peers"); - - if (peers == NULL) - goto done; - /* cppcheck-suppress uninitvar - cppcheck can't undestand these macros. */ - json_for_each_arr(i, peer, peers) { - channels = json_get_member(buffer, peer, "channels"); - if (channels == NULL) - continue; - - connected = json_get_member(buffer, peer, "connected"); - - json_for_each_arr(j, channel, channels) { - struct channel_hint h; - spendsats = json_get_member(buffer, channel, "spendable_msat"); - scid = json_get_member(buffer, channel, "short_channel_id"); + struct listpeers_channel **chans; - alias = json_get_member(buffer, channel, "alias"); - if (alias != NULL) - alias_local = json_get_member(buffer, alias, "local"); - else - alias_local = NULL; - - dir = json_get_member(buffer, channel, "direction"); - max_htlc = json_get_member(buffer, channel, "max_accepted_htlcs"); - htlcs = json_get_member(buffer, channel, "htlcs"); - state = json_get_member(buffer, channel, "state"); - if (spendsats == NULL || - (scid == NULL && alias_local == NULL) || - dir == NULL || max_htlc == NULL || state == NULL || - max_htlc->type != JSMN_PRIMITIVE || htlcs == NULL || - htlcs->type != JSMN_ARRAY) - continue; + chans = json_to_listpeers_channels(tmpctx, buffer, toks); - /* Filter out local channels if they are - * either a) disconnected, or b) not in normal - * state. */ - json_to_bool(buffer, connected, &h.enabled); - h.enabled &= json_tok_streq(buffer, state, "CHANNELD_NORMAL"); + for (size_t i = 0; i < tal_count(chans); i++) { + struct short_channel_id scid; + bool enabled; + u16 htlc_budget; - if (scid != NULL) - json_to_short_channel_id(buffer, scid, &h.scid.scid); - else - json_to_short_channel_id(buffer, alias_local, &h.scid.scid); + /* Filter out local channels if they are + * either a) disconnected, or b) not in normal + * state. */ + enabled = chans[i]->connected && streq(chans[i]->state, "CHANNELD_NORMAL"); - json_to_int(buffer, dir, &h.scid.dir); - - json_to_msat(buffer, spendsats, &h.estimated_capacity); - - /* Take the configured number of max_htlcs and - * subtract any HTLCs that might already be added to - * the channel. This is a best effort estimate and - * mostly considers stuck htlcs, concurrent payments - * may throw us off a bit. */ - json_to_u16(buffer, max_htlc, &h.htlc_budget); - h.htlc_budget -= htlcs->size; - h.local = true; + if (chans[i]->scid != NULL) + scid = *chans[i]->scid; + else + scid = *chans[i]->alias[LOCAL]; + + /* Take the configured number of max_htlcs and + * subtract any HTLCs that might already be added to + * the channel. This is a best effort estimate and + * mostly considers stuck htlcs, concurrent payments + * may throw us off a bit. */ + if (chans[i]->num_htlcs > chans[i]->max_accepted_htlcs) + htlc_budget = 0; + else + htlc_budget = chans[i]->max_accepted_htlcs - chans[i]->num_htlcs; - channel_hints_update(p, h.scid.scid, h.scid.dir, - h.enabled, true, &h.estimated_capacity, &h.htlc_budget); - } + channel_hints_update(p, scid, chans[i]->direction, enabled, true, + &chans[i]->spendable_msat, &htlc_budget); } -done: payment_continue(p); return command_still_pending(cmd); } diff --git a/plugins/libplugin.c b/plugins/libplugin.c index cebee85c67ac..55d94aa83039 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1912,7 +1912,9 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, *tmsattok = json_get_member(buffer, tok, "total_msat"), *smsattok = json_get_member(buffer, tok, "spendable_msat"), - *aliastok = json_get_member(buffer, tok, "alias"); + *aliastok = json_get_member(buffer, tok, "alias"), + *max_htlcs = json_get_member(buffer, tok, "max_accepted_htlcs"), + *htlcstok = json_get_member(buffer, tok, "htlcs"); chan = tal(ctx, struct listpeers_channel); @@ -1958,6 +1960,8 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, json_to_int(buffer, dirtok, &chan->direction); json_to_msat(buffer, tmsattok, &chan->total_msat); json_to_msat(buffer, smsattok, &chan->spendable_msat); + json_to_u16(buffer, max_htlcs, &chan->max_accepted_htlcs); + chan->num_htlcs = htlcstok->size; return chan; } @@ -1998,7 +2002,7 @@ struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx, struct listpeers_channel **chans; chans = tal_arr(ctx, struct listpeers_channel *, 0); - json_for_each_obj(i, iter, peerstok) + json_for_each_arr(i, iter, peerstok) json_add_listpeers_peer(&chans, buffer, iter); return chans; } diff --git a/plugins/libplugin.h b/plugins/libplugin.h index a846317f1bde..987406151491 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -444,6 +444,8 @@ struct listpeers_channel { int direction; struct amount_msat total_msat; struct amount_msat spendable_msat; + u16 max_accepted_htlcs; + size_t num_htlcs; /* TODO Add fields as we need them. */ }; diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index a22863508b37..00a806748586 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -122,9 +122,6 @@ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UN /* Generated stub for json_strdup */ char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_strdup called!\n"); abort(); } -/* Generated stub for json_to_bool */ -bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) -{ fprintf(stderr, "json_to_bool called!\n"); abort(); } /* Generated stub for json_to_createonion_response */ struct createonion_response *json_to_createonion_response(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, From 2ca2065d3624aa1f7087e03b138ac77d5b7d7d62 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:51:10 +1030 Subject: [PATCH 07/14] plugins: use listpeerchannels instead of listpeers. Signed-off-by: Rusty Russell --- plugins/libplugin-pay.c | 25 ++++++++++++----------- plugins/libplugin.c | 44 +++++++++++++---------------------------- plugins/libplugin.h | 2 +- 3 files changed, 28 insertions(+), 43 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 7584319bede6..5425811ed7cd 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -2347,8 +2347,8 @@ REGISTER_PAYMENT_MODIFIER(retry, struct retry_mod_data *, retry_data_init, retry_step_cb); static struct command_result * -local_channel_hints_listpeers(struct command *cmd, const char *buffer, - const jsmntok_t *toks, struct payment *p) +local_channel_hints_listpeerchannels(struct command *cmd, const char *buffer, + const jsmntok_t *toks, struct payment *p) { struct listpeers_channel **chans; @@ -2398,9 +2398,9 @@ static void local_channel_hints_cb(void *d UNUSED, struct payment *p) if (p->parent != NULL || p->step != PAYMENT_STEP_INITIALIZED) return payment_continue(p); - req = jsonrpc_request_start(p->plugin, NULL, "listpeers", - local_channel_hints_listpeers, - local_channel_hints_listpeers, p); + req = jsonrpc_request_start(p->plugin, NULL, "listpeerchannels", + local_channel_hints_listpeerchannels, + local_channel_hints_listpeerchannels, p); send_outreq(p->plugin, req); } @@ -3242,13 +3242,13 @@ static void direct_pay_override(struct payment *p) { payment_continue(p); } -/* Now that we have the listpeers result for the root payment, let's search +/* Now that we have the listpeerchannels result for the root payment, let's search * for a direct channel that is a) connected and b) in state normal. We will * check the capacity based on the channel_hints in the override. */ -static struct command_result *direct_pay_listpeers(struct command *cmd, - const char *buffer, - const jsmntok_t *toks, - struct payment *p) +static struct command_result *direct_pay_listpeerchannels(struct command *cmd, + const char *buffer, + const jsmntok_t *toks, + struct payment *p) { struct listpeers_channel **channels = json_to_listpeers_channels(tmpctx, buffer, toks); struct direct_pay_data *d = payment_mod_directpay_get_data(p); @@ -3289,8 +3289,9 @@ static void direct_pay_cb(struct direct_pay_data *d, struct payment *p) - req = jsonrpc_request_start(p->plugin, NULL, "listpeers", - direct_pay_listpeers, direct_pay_listpeers, + req = jsonrpc_request_start(p->plugin, NULL, "listpeerchannels", + direct_pay_listpeerchannels, + direct_pay_listpeerchannels, p); json_add_node_id(req->js, "id", p->destination); send_outreq(p->plugin, req); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 55d94aa83039..ff01660f23ad 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1914,10 +1914,14 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, json_get_member(buffer, tok, "spendable_msat"), *aliastok = json_get_member(buffer, tok, "alias"), *max_htlcs = json_get_member(buffer, tok, "max_accepted_htlcs"), - *htlcstok = json_get_member(buffer, tok, "htlcs"); + *htlcstok = json_get_member(buffer, tok, "htlcs"), + *idtok = json_get_member(buffer, tok, "id"), + *conntok = json_get_member(buffer, tok, "connected"); chan = tal(ctx, struct listpeers_channel); + json_to_node_id(buffer, idtok, &chan->id); + json_to_bool(buffer, conntok, &chan->connected); json_to_bool(buffer, privtok, &chan->private); chan->state = json_strdup(chan, buffer, statetok); json_to_txid(buffer, ftxidtok, &chan->funding_txid); @@ -1966,44 +1970,24 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, return chan; } -/* Append channels for this peer */ -static void json_add_listpeers_peer(struct listpeers_channel ***chans, - const char *buffer, - const jsmntok_t *tok) -{ - size_t i; - const jsmntok_t *iter; - const jsmntok_t *idtok = json_get_member(buffer, tok, "id"), - *conntok = json_get_member(buffer, tok, "connected"), - *channelstok = json_get_member(buffer, tok, "channels"); - bool connected; - struct node_id id; - - json_to_node_id(buffer, idtok, &id); - json_to_bool(buffer, conntok, &connected); - - json_for_each_arr(i, iter, channelstok) { - struct listpeers_channel *chan = json_to_listpeers_channel(*chans, buffer, iter); - if (!chan) - continue; - chan->id = id; - chan->connected = connected; - tal_arr_expand(chans, chan); - } -} - struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) { size_t i; const jsmntok_t *iter; - const jsmntok_t *peerstok = json_get_member(buffer, tok, "peers"); + const jsmntok_t *channelstok = json_get_member(buffer, tok, "channels"); struct listpeers_channel **chans; chans = tal_arr(ctx, struct listpeers_channel *, 0); - json_for_each_arr(i, iter, peerstok) - json_add_listpeers_peer(&chans, buffer, iter); + json_for_each_arr(i, iter, channelstok) { + struct listpeers_channel *chan; + + chan = json_to_listpeers_channel(chans, buffer, iter); + if (!chan) + continue; + tal_arr_expand(&chans, chan); + } return chans; } diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 987406151491..ce15ce34f04e 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -449,7 +449,7 @@ struct listpeers_channel { /* TODO Add fields as we need them. */ }; -/* Returns an array of listpeers_channel * */ +/* Returns an array of listpeers_channel from listpeerchannels * */ struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); From f3f418ea8d03d4bea948aaa852e1f0c33f44033a Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 12 Jan 2023 11:52:10 +1030 Subject: [PATCH 08/14] plugins/topology: use listpeerchannels. --- doc/lightning-listpeerchannels.7.md | 2 +- plugins/libplugin.c | 4 ++-- plugins/topology.c | 29 +++++++++++------------------ 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/doc/lightning-listpeerchannels.7.md b/doc/lightning-listpeerchannels.7.md index d20514eaffdd..5c4f1c95ee18 100644 --- a/doc/lightning-listpeerchannels.7.md +++ b/doc/lightning-listpeerchannels.7.md @@ -342,4 +342,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:adc1f36b764f1d98ba6a34b63f459a19db15fc94e37678806a1eb858a2166167) +[comment]: # ( SHA256STAMP:a9e27c78498192757d4972da091715cadd8dbf2f0c08c288e6c600626567f379) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index ff01660f23ad..c63da78a5523 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1915,8 +1915,8 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, *aliastok = json_get_member(buffer, tok, "alias"), *max_htlcs = json_get_member(buffer, tok, "max_accepted_htlcs"), *htlcstok = json_get_member(buffer, tok, "htlcs"), - *idtok = json_get_member(buffer, tok, "id"), - *conntok = json_get_member(buffer, tok, "connected"); + *idtok = json_get_member(buffer, tok, "peer_id"), + *conntok = json_get_member(buffer, tok, "peer_connected"); chan = tal(ctx, struct listpeers_channel); diff --git a/plugins/topology.c b/plugins/topology.c index f6831ccfff70..510a7901a739 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -301,25 +301,23 @@ static struct node_map *local_connected(const tal_t *ctx, const jsmntok_t *result) { size_t i; - const jsmntok_t *t, *peers = json_get_member(buf, result, "peers"); + const jsmntok_t *channel, *channels = json_get_member(buf, result, "channels"); struct node_map *connected = tal(ctx, struct node_map); node_map_init(connected); tal_add_destructor(connected, node_map_clear); - json_for_each_arr(i, t, peers) { - const jsmntok_t *chans, *c; + json_for_each_arr(i, channel, channels) { struct node_id id; bool is_connected, normal_chan; const char *err; - size_t j; - err = json_scan(tmpctx, buf, t, - "{id:%,connected:%}", + err = json_scan(tmpctx, buf, channel, + "{peer_id:%,peer_connected:%}", JSON_SCAN(json_to_node_id, &id), JSON_SCAN(json_to_bool, &is_connected)); if (err) - plugin_err(plugin, "Bad listpeers response (%s): %.*s", + plugin_err(plugin, "Bad listpeerchannels response (%s): %.*s", err, json_tok_full_len(result), json_tok_full(buf, result)); @@ -328,14 +326,9 @@ static struct node_map *local_connected(const tal_t *ctx, continue; /* Must also have a channel in CHANNELD_NORMAL */ - normal_chan = false; - chans = json_get_member(buf, t, "channels"); - json_for_each_arr(j, c, chans) { - if (json_tok_streq(buf, - json_get_member(buf, c, "state"), - "CHANNELD_NORMAL")) - normal_chan = true; - } + normal_chan = json_tok_streq(buf, + json_get_member(buf, channel, "state"), + "CHANNELD_NORMAL"); if (normal_chan) node_map_add(connected, @@ -346,7 +339,7 @@ static struct node_map *local_connected(const tal_t *ctx, } /* We want to combine local knowledge to we know which are actually inactive! */ -static struct command_result *listpeers_done(struct command *cmd, +static struct command_result *listpeerchannels_done(struct command *cmd, const char *buf, const jsmntok_t *result, struct listchannels_opts *opts) @@ -421,8 +414,8 @@ static struct command_result *json_listchannels(struct command *cmd, "Can only specify one of " "`short_channel_id`, " "`source` or `destination`"); - req = jsonrpc_request_start(cmd->plugin, cmd, "listpeers", - listpeers_done, forward_error, opts); + req = jsonrpc_request_start(cmd->plugin, cmd, "listpeerchannels", + listpeerchannels_done, forward_error, opts); return send_outreq(cmd->plugin, req); } From 3230b6bbc49bbf3cbf417d6bb561b41dd8a8f5bb Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 12 Jan 2023 11:53:10 +1030 Subject: [PATCH 09/14] contrib/pyln-testing: use listpeerchannels. --- contrib/pyln-testing/pyln/testing/utils.py | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 0e1f653de2a5..bc257e827938 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -148,8 +148,8 @@ def mine_funding_to_announce(bitcoind, nodes, num_blocks=5, wait_for_mempool=0): def wait_channel_quiescent(n1, n2): - wait_for(lambda: only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['htlcs'] == []) - wait_for(lambda: only_one(only_one(n2.rpc.listpeers(n1.info['id'])['peers'])['channels'])['htlcs'] == []) + wait_for(lambda: only_one(n1.rpc.listpeerchannels(n2.info['id'])['channels'])['htlcs'] == []) + wait_for(lambda: only_one(n2.rpc.listpeerchannels(n1.info['id'])['channels'])['htlcs'] == []) def get_tx_p2wsh_outnum(bitcoind, tx, amount): @@ -1038,29 +1038,28 @@ def channel_state(self, other): yet. """ - peers = self.rpc.listpeers(other.info['id'])['peers'] - if not peers or 'channels' not in peers[0]: + peerchannels = self.rpc.listpeerchannels(other.info['id'])['channels'] + if not peerchannels: return None - channel = peers[0]['channels'][0] + channel = peerchannels[0] return channel['state'] def get_channel_scid(self, other): """Get the short_channel_id for the channel to the other node. """ - peers = self.rpc.listpeers(other.info['id'])['peers'] - if not peers or 'channels' not in peers[0]: + peerchannels = self.rpc.listpeerchannels(other.info['id'])['channels'] + if not peerchannels: return None - channel = peers[0]['channels'][0] + channel = peerchannels[0] return channel['short_channel_id'] def get_channel_id(self, other): """Get the channel_id for the channel to the other node. """ - peers = self.rpc.listpeers(other.info['id'])['peers'] - if not peers or 'channels' not in peers[0]: + channels = self.rpc.listpeerchannels(other.info['id'])['channels'] + if len(channels) == 0: return None - channel = peers[0]['channels'][0] - return channel['channel_id'] + return channels[0]['channel_id'] def is_channel_active(self, chanid): channels = self.rpc.listchannels(chanid)['channels'] @@ -1068,7 +1067,7 @@ def is_channel_active(self, chanid): return (chanid, 0) in active and (chanid, 1) in active def wait_for_channel_onchain(self, peerid): - txid = only_one(only_one(self.rpc.listpeers(peerid)['peers'])['channels'])['scratch_txid'] + txid = only_one(self.rpc.listpeerchannels(peerid)['channels'])['scratch_txid'] wait_for(lambda: txid in self.bitcoin.rpc.getrawmempool()) def wait_channel_active(self, chanid): @@ -1102,11 +1101,12 @@ def wait_for_htlcs(self, scids=None): peers = self.rpc.listpeers()['peers'] for p, peer in enumerate(peers): if 'channels' in peer: - for c, channel in enumerate(peer['channels']): + channels_peer = self.rpc.listpeerchannels(peer['id']) + for c, channel in enumerate(channels_peer['channels']): if scids is not None and channel['short_channel_id'] not in scids: continue if 'htlcs' in channel: - wait_for(lambda: len(self.rpc.listpeers()['peers'][p]['channels'][c]['htlcs']) == 0) + wait_for(lambda: len(self.rpc.listpeerchannels(peer["id"])['channels'][c]['htlcs']) == 0) # This sends money to a directly connected peer def pay(self, dst, amt, label=None): @@ -1126,7 +1126,7 @@ def pay(self, dst, amt, label=None): assert len(invoices) == 1 and invoices[0]['status'] == 'unpaid' # Pick first normal channel. - scid = [c['short_channel_id'] for c in only_one(self.rpc.listpeers(dst_id)['peers'])['channels'] + scid = [c['short_channel_id'] for c in self.rpc.listpeerchannels(dst_id)['channels'] if c['state'] == 'CHANNELD_NORMAL'][0] routestep = { From c1a5deb60dd854b3fe00c400de07132e2160b5aa Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 12 Jan 2023 11:54:10 +1030 Subject: [PATCH 10/14] tests/utils.py: use listpeerchannels. --- tests/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index f1c338d6d9e1..cf443041bf81 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -410,11 +410,11 @@ def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=No def first_channel_id(n1, n2): - return only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['channel_id'] + return only_one(n1.rpc.listpeerchannels(n2.info['id'])['channels'])['channel_id'] def first_scid(n1, n2): - return only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['short_channel_id'] + return only_one(n1.rpc.listpeerchannels(n2.info['id'])['channels'])['short_channel_id'] def basic_fee(feerate): From fcf6484ff121814b80d4f7e293ef252db7f70299 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 12 Jan 2023 11:55:55 +1030 Subject: [PATCH 11/14] tests: use listpeerchannels. --- tests/test_bookkeeper.py | 2 +- tests/test_closing.py | 101 ++++++++++----------- tests/test_connection.py | 183 +++++++++++++++++++++------------------ tests/test_db.py | 4 +- tests/test_gossip.py | 6 +- tests/test_invoices.py | 4 +- tests/test_misc.py | 8 +- tests/test_opening.py | 97 ++++++++++----------- tests/test_pay.py | 142 +++++++++++++++--------------- tests/test_plugin.py | 36 ++++---- tests/test_wallet.py | 6 +- 11 files changed, 300 insertions(+), 289 deletions(-) diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 84088cb33529..ed31c27e91bf 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -703,7 +703,7 @@ def test_rebalance_tracking(node_factory, bitcoind): wait_for(lambda: 'invoice' not in [ev['tag'] for ev in l1.rpc.bkpr_listincome()['income_events']]) inc_evs = l1.rpc.bkpr_listincome()['income_events'] - outbound_chan_id = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['channel_id'] + outbound_chan_id = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['channel_id'] outbound_ev = only_one([ev for ev in inc_evs if ev['tag'] == 'rebalance_fee']) assert outbound_ev['account'] == outbound_chan_id diff --git a/tests/test_closing.py b/tests/test_closing.py index d5839f357445..8f3d25433266 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -32,16 +32,16 @@ def test_closing_simple(node_factory, bitcoind, chainparams): assert bitcoind.rpc.getmempoolinfo()['size'] == 0 - billboard = only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] + billboard = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['status'] assert billboard == ['CHANNELD_NORMAL:Channel ready for use.'] - billboard = only_one(l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'])['status'] + billboard = only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['status'] assert billboard == ['CHANNELD_NORMAL:Channel ready for use.'] bitcoind.generate_block(5) wait_for(lambda: len(l1.getactivechannels()) == 2) wait_for(lambda: len(l2.getactivechannels()) == 2) - billboard = only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] + billboard = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['status'] # This may either be from a local_update or an announce, so just # check for the substring assert 'CHANNELD_NORMAL:Channel ready for use.' in billboard[0] @@ -67,7 +67,7 @@ def test_closing_simple(node_factory, bitcoind, chainparams): # Now grab the close transaction closetxid = only_one(bitcoind.rpc.getrawmempool(False)) - billboard = only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] + billboard = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['status'] assert billboard == [ 'CLOSINGD_SIGEXCHANGE:We agreed on a closing fee of {} satoshi for tx:{}'.format(fee, closetxid), ] @@ -80,14 +80,14 @@ def test_closing_simple(node_factory, bitcoind, chainparams): assert closetxid in set([o['txid'] for o in l1.rpc.listfunds()['outputs']]) assert closetxid in set([o['txid'] for o in l2.rpc.listfunds()['outputs']]) - wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] == [ + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['status'] == [ 'CLOSINGD_SIGEXCHANGE:We agreed on a closing fee of {} satoshi for tx:{}'.format(fee, closetxid), 'ONCHAIN:Tracking mutual close transaction', 'ONCHAIN:All outputs resolved: waiting 99 more blocks before forgetting channel' ]) bitcoind.generate_block(9) - wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] == [ + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['status'] == [ 'CLOSINGD_SIGEXCHANGE:We agreed on a closing fee of {} satoshi for tx:{}'.format(fee, closetxid), 'ONCHAIN:Tracking mutual close transaction', 'ONCHAIN:All outputs resolved: waiting 90 more blocks before forgetting channel' @@ -172,12 +172,12 @@ def test_closing_id(node_factory): # Close by full channel ID. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, 10**6) - cid = l2.rpc.listpeers()['peers'][0]['channels'][0]['channel_id'] + cid = l2.rpc.listpeerchannels()['channels'][0]['channel_id'] l2.rpc.close(cid) # Technically, l2 disconnects before l1 finishes analyzing the final msg. # Wait for them to both consider it closed! - wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']])) - wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']])) + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']])) + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in l2.rpc.listpeerchannels(l1.info['id'])['channels']])) # Close by peer ID. l2.rpc.connect(l1.info['id'], 'localhost', l1.port) @@ -185,8 +185,8 @@ def test_closing_id(node_factory): l2.fundchannel(l1, 10**6) pid = l1.info['id'] l2.rpc.close(pid) - wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']])) - wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']])) + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']])) + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in l2.rpc.listpeerchannels(l1.info['id'])['channels']])) @unittest.skipIf(TEST_NETWORK != 'regtest', 'FIXME: broken under elements') @@ -245,7 +245,7 @@ def test_closing_different_fees(node_factory, bitcoind, executor): bitcoind.generate_block(1) for p in peers: p.daemon.wait_for_log(' to ONCHAIN') - wait_for(lambda: 'ONCHAIN:Tracking mutual close transaction' in only_one(p.rpc.listpeers(l1.info['id'])['peers'][0]['channels'])['status']) + wait_for(lambda: 'ONCHAIN:Tracking mutual close transaction' in only_one(p.rpc.listpeerchannels(l1.info['id'])['channels'])['status']) l1.daemon.wait_for_logs([' to ONCHAIN'] * num_peers) @@ -306,7 +306,7 @@ def test_closing_specified_destination(node_factory, bitcoind, chainparams): # Now grab the close transaction closetxs = {} for i, n in enumerate([l2, l3, l4]): - billboard = only_one(l1.rpc.listpeers(n.info['id'])['peers'][0]['channels'])['status'][0] + billboard = only_one(l1.rpc.listpeerchannels(n.info['id'])['channels'])['status'][0] m = re.search(r'CLOSINGD_SIGEXCHANGE.* tx:([a-f0-9]{64})', billboard) closetxs[n] = m.group(1) @@ -371,8 +371,7 @@ def feerate_for(target, minimum=0, maximum=10000000): def get_fee_from_status(node, peer_id, i): nonlocal fees_from_status - peer = only_one(node.rpc.listpeers(peer_id)['peers']) - channel = only_one(peer['channels']) + channel = only_one(node.rpc.listpeerchannels(peer_id)['channels']) status = channel['status'][0] m = status_agreed_regex.search(status) @@ -551,7 +550,7 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): bitcoind.generate_block(100) sync_blockheight(bitcoind, [l1, l2]) - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) + wait_for(lambda: l2.rpc.listpeerchannels()['channels'] == []) # Do one last pass over the logs to extract the reactions l2 sent l2.daemon.logsearch_start = needle @@ -680,7 +679,8 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): bitcoind.generate_block(100) sync_blockheight(bitcoind, [l1, l2]) - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) + peer = only_one(l2.rpc.listpeers()["peers"]) + wait_for(lambda: l2.rpc.listpeerchannels(peer["id"])['channels'] == []) # Do one last pass over the logs to extract the reactions l2 sent l2.daemon.logsearch_start = needle @@ -801,7 +801,8 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): est_fees = calc_lease_fee(amount, feerate, rates) # This should be the accepter's amount - fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] + peer = only_one(l1.rpc.listpeers()["peers"]) + fundings = only_one(l1.rpc.listpeerchannels(peer["id"])['channels'])['funding'] assert Millisatoshi(amount * 1000) == fundings['remote_funds_msat'] assert Millisatoshi(est_fees + amount * 1000) == fundings['local_funds_msat'] assert Millisatoshi(est_fees) == fundings['fee_paid_msat'] @@ -818,7 +819,8 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): # make sure it's completely resolved before we generate blocks, # otherwise it can close HTLC! - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['htlcs'] == []) + peer = only_one(l2.rpc.listpeers()["peers"]) + wait_for(lambda: only_one(l2.rpc.listpeerchannels(peer["id"])['channels'])['htlcs'] == []) # l2 attempts to close a channel that it leased, should fail with pytest.raises(RpcError, match=r'Peer leased this channel from us'): @@ -927,7 +929,8 @@ def test_channel_lease_unilat_closes(node_factory, bitcoind): est_fees = calc_lease_fee(amount, feerate, rates) # This should be the accepter's amount - fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] + peer = only_one(l1.rpc.listpeers()["peers"]) + fundings = only_one(l1.rpc.listpeerchannels(peer["id"])['channels'])['funding'] assert Millisatoshi(amount * 1000) == Millisatoshi(fundings['remote_funds_msat']) assert Millisatoshi(est_fees + amount * 1000) == Millisatoshi(fundings['local_funds_msat']) @@ -1206,7 +1209,7 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): l4.rpc.sendpay(route, sticky_inv['payment_hash'], payment_secret=sticky_inv['payment_secret']) l1.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') - wait_for(lambda: len(l2.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0]['htlcs']) == 1) + wait_for(lambda: len(l2.rpc.listpeerchannels(l3.info['id'])['channels'][0]['htlcs']) == 1) # make database snapshot of l2 l2.stop() @@ -1397,7 +1400,7 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): l4.rpc.sendpay(route, sticky_inv_2['payment_hash'], payment_secret=sticky_inv_2['payment_secret']) l1.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') - wait_for(lambda: len(l2.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0]['htlcs']) == 2) + wait_for(lambda: len(l2.rpc.listpeerchannels(l3.info['id'])['channels'][0]['htlcs']) == 2) # make database snapshot of l2 l2.stop() @@ -2717,10 +2720,9 @@ def test_onchain_different_fees(node_factory, bitcoind, executor): # Now, 100 blocks it should be done. bitcoind.generate_block(100) - # May reconnect, may not: if not, peer does not exist! - wait_for(lambda: all(p['channels'] == [] for p in l1.rpc.listpeers()['peers'])) - wait_for(lambda: all(p['channels'] == [] for p in l2.rpc.listpeers()['peers'])) + wait_for(lambda: l1.rpc.listpeerchannels()['channels'] == []) + wait_for(lambda: l2.rpc.listpeerchannels()['channels'] == []) @pytest.mark.developer("needs DEVELOPER=1") @@ -2799,9 +2801,9 @@ def setup_multihtlc_test(node_factory, bitcoind): # Make sure they're all in normal state. bitcoind.generate_block(1) - wait_for(lambda: all([only_one(p['channels'])['state'] == 'CHANNELD_NORMAL' + wait_for(lambda: all([only_one(l4.rpc.listpeerchannels(p["id"])['channels'])['state'] == 'CHANNELD_NORMAL' for p in l4.rpc.listpeers()['peers']])) - wait_for(lambda: all([only_one(p['channels'])['state'] == 'CHANNELD_NORMAL' + wait_for(lambda: all([only_one(l5.rpc.listpeerchannels(p["id"])['channels'])['state'] == 'CHANNELD_NORMAL' for p in l5.rpc.listpeers()['peers']])) # Balance them @@ -2882,7 +2884,7 @@ def route_to_l1(src): l1.daemon.wait_for_logs(['peer_in WIRE_UPDATE_ADD_HTLC'] * 4) # We have 6 HTLCs trapped in l4-l5 channel. - assert len(only_one(only_one(l4.rpc.listpeers(l5.info['id'])['peers'])['channels'])['htlcs']) == 6 + assert len(only_one(l4.rpc.listpeerchannels(l5.info['id'])['channels'])['htlcs']) == 6 # We are all connected. for n in l1, l2, l3, l4, l5, l6, l7: @@ -3144,12 +3146,12 @@ def test_permfail(node_factory, bitcoind): l2.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log('Propose handling OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET (.*) after 5 blocks') - wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['status'] == ['ONCHAIN:Tracking their unilateral close', 'ONCHAIN:All outputs resolved: waiting 99 more blocks before forgetting channel']) def check_billboard(): - billboard = only_one(l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'])['status'] + billboard = only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['status'] return ( len(billboard) == 2 and billboard[0] == 'ONCHAIN:Tracking our own unilateral close' @@ -3176,7 +3178,7 @@ def check_billboard(): bitcoind.generate_block(95) wait_for(lambda: l1.rpc.listpeers()['peers'] == []) - wait_for(lambda: only_one(l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'])['status'] == [ + wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['status'] == [ 'ONCHAIN:Tracking our own unilateral close', 'ONCHAIN:All outputs resolved: waiting 5 more blocks before forgetting channel' ]) @@ -3234,8 +3236,8 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): l2.rpc.close(l1.info['id'], unilateraltimeout=1) bitcoind.generate_block(1, wait_for_mempool=1) fut.result(TIMEOUT) - wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN']) - wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN']) + wait_for(lambda: [c['state'] for c in l1.rpc.listpeerchannels()['channels']] == ['ONCHAIN']) + wait_for(lambda: [c['state'] for c in l2.rpc.listpeerchannels()['channels']] == ['ONCHAIN']) # Works when l2 closes channel, too. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -3250,8 +3252,8 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): fut.result(TIMEOUT) bitcoind.generate_block(1, wait_for_mempool=1) - wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN', 'ONCHAIN']) - wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN', 'ONCHAIN']) + wait_for(lambda: [c['state'] for c in l1.rpc.listpeerchannels()['channels']] == ['ONCHAIN', 'ONCHAIN']) + wait_for(lambda: [c['state'] for c in l2.rpc.listpeerchannels()['channels']] == ['ONCHAIN', 'ONCHAIN']) # Figure out what address it will try to use. keyidx = int(l1.db_query("SELECT intval FROM vars WHERE name='bip32_max_index';")[0]['intval']) @@ -3273,7 +3275,7 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.fundchannel(l2.info['id'], 1000000) l1.rpc.close(l2.info['id']) - wait_for(lambda: sorted([c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']]) == ['CLOSINGD_COMPLETE', 'ONCHAIN', 'ONCHAIN']) + wait_for(lambda: sorted([c['state'] for c in l1.rpc.listpeerchannels()['channels']]) == ['CLOSINGD_COMPLETE', 'ONCHAIN', 'ONCHAIN']) @pytest.mark.developer("needs to set upfront_shutdown_script") @@ -3401,8 +3403,8 @@ def test_closing_higherfee(node_factory, bitcoind, executor): fut.result(TIMEOUT) # But we still complete negotiation! - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'][0]['state'] == 'CLOSINGD_COMPLETE') - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'][0]['state'] == 'CLOSINGD_COMPLETE') + wait_for(lambda: l1.rpc.listpeerchannels()['channels'][0]['state'] == 'CLOSINGD_COMPLETE') + wait_for(lambda: l2.rpc.listpeerchannels()['channels'][0]['state'] == 'CLOSINGD_COMPLETE') @unittest.skipIf(True, "Test is extremely flaky") @@ -3438,8 +3440,8 @@ def test_htlc_rexmit_while_closing(node_factory, executor): # Now l2 should be in CLOSINGD_SIGEXCHANGE, l1 still waiting on # WIRE_REVOKE_AND_ACK. - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE') - assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CHANNELD_SHUTTING_DOWN' + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE') + assert only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_SHUTTING_DOWN' # They don't realize they're not talking, so disconnect and reconnect. l1.rpc.disconnect(l2.info['id'], force=True) @@ -3468,8 +3470,8 @@ def test_you_forgot_closed_channel(node_factory, executor): fut = executor.submit(l1.rpc.close, l2.info['id']) # l2 considers the closing done, l1 does not - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_COMPLETE') - assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == 'CLOSINGD_COMPLETE') + assert only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' # l1 won't send anything else until we reconnect, then it should succeed. l1.rpc.disconnect(l2.info['id'], force=True) @@ -3493,8 +3495,8 @@ def test_you_forgot_closed_channel_onchain(node_factory, bitcoind, executor): fut = executor.submit(l1.rpc.close, l2.info['id']) # l2 considers the closing done, l1 does not - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_COMPLETE') - assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == 'CLOSINGD_COMPLETE') + assert only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' # l1 does not see any new blocks. def no_new_blocks(req): @@ -3505,7 +3507,7 @@ def no_new_blocks(req): # Close transaction mined bitcoind.generate_block(1, wait_for_mempool=1) - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'ONCHAIN') + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == 'ONCHAIN') # l1 reconnects, it should succeed. l1.rpc.disconnect(l2.info['id'], force=True) @@ -3538,11 +3540,11 @@ def test_segwit_anyshutdown(node_factory, bitcoind, executor): # because the resulting tx is too small! Balance channel so close # has two outputs. bitcoind.generate_block(1, wait_for_mempool=1) - wait_for(lambda: any([c['state'] == 'CHANNELD_NORMAL' for c in only_one(l1.rpc.listpeers()['peers'])['channels']])) + wait_for(lambda: any([c['state'] == 'CHANNELD_NORMAL' for c in l1.rpc.listpeerchannels()['channels']])) l1.pay(l2, 10**9 // 2) l1.rpc.close(l2.info['id'], destination=addr) bitcoind.generate_block(1, wait_for_mempool=1) - wait_for(lambda: all([c['state'] == 'ONCHAIN' for c in only_one(l1.rpc.listpeers()['peers'])['channels']])) + wait_for(lambda: all([c['state'] == 'ONCHAIN' for c in l1.rpc.listpeerchannels()['channels']])) @pytest.mark.developer("needs to manipulate features") @@ -3565,7 +3567,7 @@ def test_anysegwit_close_needs_feature(node_factory, bitcoind): # Now it will work! l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.close(l2.info['id'], destination='bcrt1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k0ylj56') - wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_COMPLETE') + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CLOSINGD_COMPLETE') bitcoind.generate_block(1, wait_for_mempool=1) @@ -3734,7 +3736,8 @@ def ignore_sendrawtx(r): l2.stop() l1.rpc.close(l2.info['id'], unilateraltimeout=1) - wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'AWAITING_UNILATERAL') + peer = only_one(l1.rpc.listpeers()["peers"]) + wait_for(lambda: only_one(l1.rpc.listpeerchannels(peer["id"])['channels'])['state'] == 'AWAITING_UNILATERAL') l1.stop() assert bitcoind.rpc.getrawmempool() == [] diff --git a/tests/test_connection.py b/tests/test_connection.py index c46fb6635bfa..ab6c34b73975 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -28,8 +28,8 @@ def test_connect_basic(node_factory): # These should be in openingd. assert l1.rpc.getpeer(l2.info['id'])['connected'] assert l2.rpc.getpeer(l1.info['id'])['connected'] - assert len(l1.rpc.getpeer(l2.info['id'])['channels']) == 0 - assert len(l2.rpc.getpeer(l1.info['id'])['channels']) == 0 + assert len(l1.rpc.listpeerchannels(l2.info['id'])['channels']) == 0 + assert len(l2.rpc.listpeerchannels(l1.info['id'])['channels']) == 0 # Reconnect should be a noop ret = l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) @@ -268,8 +268,8 @@ def test_connection_moved(node_factory, executor): def test_balance(node_factory): l1, l2 = node_factory.line_graph(2, fundchannel=True) - p1 = only_one(l1.rpc.getpeer(peer_id=l2.info['id'], level='info')['channels']) - p2 = only_one(l2.rpc.getpeer(l1.info['id'], 'info')['channels']) + p1 = only_one(l1.rpc.listpeerchannels(peer_id=l2.info['id'])['channels']) + p2 = only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels']) assert p1['to_us_msat'] == 10**6 * 1000 assert p1['total_msat'] == 10**6 * 1000 assert p2['to_us_msat'] == 0 @@ -406,8 +406,7 @@ def test_channel_abandon(node_factory, bitcoind): bitcoind.generate_block(1, wait_for_mempool=withdraw['txid']) # FIXME: lightningd should notice channel will never now open! - print(l1.rpc.listpeers()) - assert (only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] + assert (only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_AWAITING_LOCKIN') @@ -579,7 +578,7 @@ def test_disconnect_half_signed(node_factory): # Peer remembers, opener doesn't. wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) - assert len(only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']) == 1 + assert len(l2.rpc.listpeerchannels(l1.info['id'])['channels']) == 1 @pytest.mark.developer @@ -923,7 +922,7 @@ def no_blocks_above(req): l1.restart() # l2 will now uses (REMOTE's) announcement_signatures it has stored - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'][0]['channels'])['status'] == [ + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', 'CHANNELD_NORMAL:Channel ready for use. Channel announced.']) @@ -970,8 +969,8 @@ def test_shutdown_awaiting_lockin(node_factory, bitcoind): bitcoind.generate_block(100) # Won't disconnect! - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) + wait_for(lambda: l1.rpc.listpeerchannels()['channels'] == []) + wait_for(lambda: l2.rpc.listpeerchannels()['channels'] == []) @pytest.mark.openchannel('v1') @@ -1316,7 +1315,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): l1.rpc.disconnect(l2.info['id'], force=True) wait_for(lambda: not only_one(l1.rpc.listpeers()['peers'])['connected']) - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_AWAITING_LOCKIN') assert l1.rpc.fundchannel_cancel(l2.info['id'])['cancelled'] @@ -1597,7 +1596,7 @@ def has_normal_channels(l1, l2): return False return any([c['state'] == 'CHANNELD_AWAITING_LOCKIN' or c['state'] == 'CHANNELD_NORMAL' - for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']]) + for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']]) def _fundchannel(l1, l2, amount, close_to): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -1614,7 +1613,7 @@ def _fundchannel(l1, l2, amount, close_to): assert 'close_to' not in resp for node in [l1, l2]: - channel = node.rpc.listpeers()['peers'][0]['channels'][-1] + channel = node.rpc.listpeerchannels()['channels'][-1] assert amount * 1000 == channel['total_msat'] def _close(src, dst, addr=None): @@ -1640,21 +1639,21 @@ def _close(src, dst, addr=None): # check that you can provide a closing address upfront addr = l1.rpc.newaddr()['bech32'] _fundchannel(l1, l2, amt_normal, addr) - # confirm that it appears in listpeers - assert addr == only_one(l1.rpc.listpeers()['peers'])['channels'][1]['close_to_addr'] + # confirm that it appears in listpeerchannels + assert addr == l1.rpc.listpeerchannels()['channels'][1]['close_to_addr'] assert _close(l1, l2) == [addr] # check that passing in the same addr to close works addr = bitcoind.rpc.getnewaddress() _fundchannel(l1, l2, amt_normal, addr) - assert addr == only_one(l1.rpc.listpeers()['peers'])['channels'][2]['close_to_addr'] + assert addr == l1.rpc.listpeerchannels()['channels'][2]['close_to_addr'] assert _close(l1, l2, addr) == [addr] # check that remote peer closing works as expected (and that remote's close_to works) _fundchannel(l1, l2, amt_addr, addr) # send some money to remote so that they have a closeout l1.rpc.pay(l2.rpc.invoice((amt_addr // 2) * 1000, 'test_remote_close_to', 'desc')['bolt11']) - assert only_one(l2.rpc.listpeers()['peers'])['channels'][-1]['close_to_addr'] == remote_valid_addr + assert l2.rpc.listpeerchannels()['channels'][-1]['close_to_addr'] == remote_valid_addr # The tx outputs must be one of the two permutations assert _close(l2, l1) in ([addr, remote_valid_addr], [remote_valid_addr, addr]) @@ -1682,8 +1681,11 @@ def test_funding_external_wallet(node_factory, bitcoind): # Peer should still be connected and in state waiting for funding_txid assert peer['id'] == l2.info['id'] r = re.compile('Funding channel start: awaiting funding_txid with output to .*') - assert any(r.match(line) for line in peer['channels'][0]['status']) - assert 'OPENINGD' in peer['channels'][0]['state'] + + channels = l1.rpc.listpeerchannels(peer['id'])['channels'] + assert len(channels) == 1, f"Channels for peer {peer['id']} need to be not empty" + assert any(r.match(line) for line in channels[0]['status']) + assert 'OPENINGD' in channels[0]['state'] # Trying to start a second funding should not work, it's in progress. with pytest.raises(RpcError, match=r'Already funding channel'): @@ -1712,7 +1714,7 @@ def test_funding_external_wallet(node_factory, bitcoind): for node in [l1, l2]: node.daemon.wait_for_log(r'State changed from CHANNELD_AWAITING_LOCKIN to CHANNELD_NORMAL') - channel = node.rpc.listpeers()['peers'][0]['channels'][0] + channel = node.rpc.listpeerchannels()['channels'][0] assert amount * 1000 == channel['total_msat'] # Test that we don't crash if peer disconnects after fundchannel_start @@ -2012,14 +2014,14 @@ def _connect_str(node): expected_fee = int(funding_tx_feerate[:-5]) * weight // 1000 assert expected_fee == entry['fees']['base'] * 10 ** 8 - assert only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['feerate']['perkw'] == commitment_tx_feerate_int - assert only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['feerate']['perkb'] == commitment_tx_feerate_int * 4 + assert only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['feerate']['perkw'] == commitment_tx_feerate_int + assert only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['feerate']['perkb'] == commitment_tx_feerate_int * 4 - txfee = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['last_tx_fee_msat'] + txfee = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['last_tx_fee_msat'] # We get the expected close txid, force close the channel, then fish # the details about the transaction out of the mempoool entry - close_txid = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['scratch_txid'] + close_txid = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['scratch_txid'] l1.rpc.dev_fail(l2.info['id']) l1.wait_for_channel_onchain(l2.info['id']) entry = bitcoind.rpc.getmempoolentry(close_txid) @@ -2134,10 +2136,7 @@ def test_multifunding_best_effort(node_factory, bitcoind): # open again, so multiple channels may remain # listed. def get_funded_channel_scid(n1, n2): - peers = n1.rpc.listpeers(n2.info['id'])['peers'] - assert len(peers) == 1 - peer = peers[0] - channels = peer['channels'] + channels = n1.rpc.listpeerchannels(n2.info['id'])['channels'] assert channels for c in channels: state = c['state'] @@ -2236,8 +2235,8 @@ def test_channel_persistence(node_factory, bitcoind, executor): l1.fundchannel(l2, 100000) - peers = l1.rpc.listpeers()['peers'] - assert(only_one(peers[0]['channels'])['state'] == 'CHANNELD_NORMAL') + channels = l1.rpc.listpeerchannels()['channels'] + assert(only_one(channels)['state'] == 'CHANNELD_NORMAL') # Both nodes should now have exactly one channel in the database for n in (l1, l2): @@ -2257,14 +2256,14 @@ def test_channel_persistence(node_factory, bitcoind, executor): del l2.daemon.opts['dev-disable-commit-after'] # Wait for l1 to notice - wait_for(lambda: 'connected' not in only_one(l1.rpc.listpeers()['peers'][0]['channels'])) + wait_for(lambda: 'connected' not in l1.rpc.listpeerchannels()['channels']) # Now restart l2 and it should reload peers/channels from the DB l2.start() wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 1) # Wait for the restored HTLC to finish - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 99990000) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['to_us_msat'] == 99990000) wait_for(lambda: len([p for p in l1.rpc.listpeers()['peers'] if p['connected']])) wait_for(lambda: len([p for p in l2.rpc.listpeers()['peers'] if p['connected']])) @@ -2274,12 +2273,12 @@ def test_channel_persistence(node_factory, bitcoind, executor): # L1 doesn't actually update to_us_msat until it receives # revoke_and_ack from L2, which can take a little bit. - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 99980000) - assert only_one(l2.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 20000 + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['to_us_msat'] == 99980000) + assert only_one(l2.rpc.listpeerchannels()['channels'])['to_us_msat'] == 20000 # Finally restart l1, and make sure it remembers l1.restart() - assert only_one(l1.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 99980000 + assert only_one(l1.rpc.listpeerchannels()['channels'])['to_us_msat'] == 99980000 # Keep l1 from sending its onchain tx def censoring_sendrawtx(r): @@ -2315,9 +2314,9 @@ def test_private_channel(node_factory): assert not l2.daemon.is_in_log('Received node_announcement for node {}'.format(l1.info['id'])) # test for 'private' flag in rpc output - assert only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['private'] + assert only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['private'] # check non-private channel - assert not only_one(only_one(l4.rpc.listpeers(l3.info['id'])['peers'])['channels'])['private'] + assert not only_one(l4.rpc.listpeerchannels(l3.info['id'])['channels'])['private'] @pytest.mark.developer("gossip without DEVELOPER=1 is slow") @@ -2400,8 +2399,8 @@ def test_fee_limits(node_factory, bitcoind): l1.daemon.wait_for_log('Peer transient failure in CHANNELD_NORMAL: channeld WARNING: .*: update_fee 253 outside range 1875-75000') # Closes, but does not error. Make sure it's noted in their status though. - assert 'update_fee 253 outside range 1875-75000' in only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['status'][0] - assert 'update_fee 253 outside range 1875-75000' in only_one(only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels'])['status'][0] + assert 'update_fee 253 outside range 1875-75000' in only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['status'][0] + assert 'update_fee 253 outside range 1875-75000' in only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['status'][0] # Make l2 accept those fees, and it should recover. l2.stop() @@ -2570,7 +2569,7 @@ def test_multiple_channels(node_factory): r'State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE' ) - channels = only_one(l1.rpc.listpeers()['peers'])['channels'] + channels = l1.rpc.listpeerchannels()['channels'] assert len(channels) == 3 # Most in state ONCHAIN, last is CLOSINGD_COMPLETE for i in range(len(channels) - 1): @@ -2598,7 +2597,7 @@ def test_forget_channel(node_factory): # Forcing should work l1.rpc.dev_forget_channel(l2.info['id'], True) - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) + wait_for(lambda: l1.rpc.listpeerchannels()['channels'] == []) # And restarting should keep that peer forgotten l1.restart() @@ -2623,7 +2622,7 @@ def test_peerinfo(node_factory, bitcoind): # Gossiping but no node announcement yet assert l1.rpc.getpeer(l2.info['id'])['connected'] - assert len(l1.rpc.getpeer(l2.info['id'])['channels']) == 0 + assert len(l1.rpc.listpeerchannels(l2.info['id'])['channels']) == 0 assert l1.rpc.getpeer(l2.info['id'])['features'] == lfeatures # Fund a channel to force a node announcement @@ -2658,8 +2657,8 @@ def test_peerinfo(node_factory, bitcoind): bitcoind.generate_block(100, wait_for_mempool=1) l1.daemon.wait_for_log('onchaind complete, forgetting peer') l2.daemon.wait_for_log('onchaind complete, forgetting peer') - assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'] == [] - assert only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels'] == [] + assert l1.rpc.listpeerchannels(l2.info['id'])['channels'] == [] + assert l2.rpc.listpeerchannels(l1.info['id'])['channels'] == [] # The only channel was closed, everybody should have forgotten the nodes assert l1.rpc.listnodes()['nodes'] == [] @@ -2673,9 +2672,9 @@ def test_disconnectpeer(node_factory, bitcoind): # Gossiping assert l1.rpc.getpeer(l2.info['id'])['connected'] - assert len(l1.rpc.getpeer(l2.info['id'])['channels']) == 0 + assert len(l1.rpc.listpeerchannels(l2.info['id'])['channels']) == 0 assert l1.rpc.getpeer(l3.info['id'])['connected'] - assert len(l1.rpc.getpeer(l3.info['id'])['channels']) == 0 + assert len(l1.rpc.listpeerchannels(l3.info['id'])['channels']) == 0 wait_for(lambda: l2.rpc.getpeer(l1.info['id']) is not None) # Disconnect l2 from l1 @@ -2745,7 +2744,7 @@ def mock_donothing(r): l2.daemon.wait_for_log(r'Forgetting channel: It has been {}\d blocks'.format(str(blocks)[:-1])) # fundee will also forget, but not disconnect from peer. - wait_for(lambda: only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels'] == []) + wait_for(lambda: l2.rpc.listpeerchannels(l1.info['id'])['channels'] == []) @pytest.mark.developer("needs --dev-max-funding-unconfirmed-blocks") @@ -2783,7 +2782,7 @@ def mock_donothing(r): bitcoind.generate_block(1, wait_for_mempool=1) # Check that l1 opened the channel - wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL') end_amount = only_one(l1.rpc.listfunds()['outputs'])['amount_msat'] # We should be out the onchaind fees assert start_amount > end_amount + Millisatoshi(10 ** 7 * 100) @@ -2840,8 +2839,8 @@ def test_no_fee_estimate(node_factory, bitcoind, executor): l1.daemon.wait_for_log('Failing due to dev-fail command') l1.wait_for_channel_onchain(l2.info['id']) bitcoind.generate_block(6) - wait_for(lambda: only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['state'] == 'ONCHAIN') - wait_for(lambda: only_one(l2.rpc.getpeer(l1.info['id'])['channels'])['state'] == 'ONCHAIN') + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['state'] == 'ONCHAIN') + wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['state'] == 'ONCHAIN') # But can accept incoming connections. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -3462,10 +3461,10 @@ def test_wumbo_channels(node_factory, bitcoind): l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) l1.rpc.fundchannel(l2.info['id'], 'all') bitcoind.generate_block(1, wait_for_mempool=1) - wait_for(lambda: 'CHANNELD_NORMAL' in [c['state'] for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']]) + wait_for(lambda: 'CHANNELD_NORMAL' in [c['state'] for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']]) # Exact amount depends on fees, but it will be wumbo! - chan = only_one([c for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'] if c['state'] == 'CHANNELD_NORMAL']) + chan = only_one([c for c in l1.rpc.listpeerchannels(l2.info['id'])['channels'] if c['state'] == 'CHANNELD_NORMAL']) amount = chan['funding']['local_funds_msat'] assert amount > Millisatoshi(str((1 << 24) - 1) + "sat") @@ -3474,7 +3473,7 @@ def test_wumbo_channels(node_factory, bitcoind): assert spendable > Millisatoshi(str((1 << 24) - 1) + "sat") # So should peer. - chan = only_one([c for c in only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels'] if c['state'] == 'CHANNELD_NORMAL']) + chan = only_one([c for c in l2.rpc.listpeerchannels(l1.info['id'])['channels'] if c['state'] == 'CHANNELD_NORMAL']) assert chan['receivable_msat'] == spendable # And we can wumbo pay, right? @@ -3501,26 +3500,26 @@ def test_channel_features(node_factory, bitcoind): l1.rpc.fundchannel(l2.info['id'], 'all') # We should see features in unconfirmed channels. - chan = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) + chan = only_one(l1.rpc.listpeerchannels()['channels']) assert 'option_static_remotekey' in chan['features'] if EXPERIMENTAL_FEATURES or l1.config('experimental-dual-fund'): assert 'option_anchor_outputs' in chan['features'] # l2 should agree. - assert only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['features'] == chan['features'] + assert only_one(l2.rpc.listpeerchannels()['channels'])['features'] == chan['features'] # Confirm it. bitcoind.generate_block(1) - wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CHANNELD_NORMAL') - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL') - chan = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) + chan = only_one(l1.rpc.listpeerchannels()['channels']) assert 'option_static_remotekey' in chan['features'] if EXPERIMENTAL_FEATURES or l1.config('experimental-dual-fund'): assert 'option_anchor_outputs' in chan['features'] # l2 should agree. - assert only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['features'] == chan['features'] + assert only_one(l2.rpc.listpeerchannels()['channels'])['features'] == chan['features'] @pytest.mark.developer("need dev-force-features") @@ -3531,7 +3530,7 @@ def test_nonstatic_channel(node_factory, bitcoind): # needs at least 15 to connect # (and 9 is a dependent) {'dev-force-features': '9,15////////'}]) - chan = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) + chan = only_one(l1.rpc.listpeerchannels()['channels']) assert 'option_static_remotekey' not in chan['features'] assert 'option_anchor_outputs' not in chan['features'] @@ -3712,8 +3711,8 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): l2.wait_for_onchaind_broadcast('OUR_PENALTY_TX', 'THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM') bitcoind.generate_block(100) - # This works even if they disconnect and listpeers() is empty: - wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) + # This works even if they disconnect and listpeerchannels() is empty: + wait_for(lambda: l2.rpc.listpeerchannels()['channels'] == []) # TEST 2: Cheat from post-upgrade. node_factory.join_nodes([l1, l2]) @@ -3738,7 +3737,7 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): 'THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM') bitcoind.generate_block(100) # This works even if they disconnect and listpeers() is empty: - wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) + wait_for(lambda: len(l2.rpc.listpeerchannels()['channels']) == 0) # TEST 3: Unilateral close from pre-upgrade node_factory.join_nodes([l1, l2]) @@ -3766,8 +3765,8 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): bitcoind.generate_block(5) bitcoind.generate_block(100, wait_for_mempool=1) - # This works even if they disconnect and listpeers() is empty: - wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) + # This works even if they disconnect and listpeerchannels() is empty: + wait_for(lambda: len(l2.rpc.listpeerchannels()['channels']) == 0) # TEST 4: Unilateral close from post-upgrade node_factory.join_nodes([l1, l2]) @@ -3792,8 +3791,8 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): bitcoind.generate_block(5) bitcoind.generate_block(100, wait_for_mempool=1) - # This works even if they disconnect and listpeers() is empty: - wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) + # This works even if they disconnect and listpeerchannels() is empty: + wait_for(lambda: len(l2.rpc.listpeerchannels()['channels']) == 0) @unittest.skipIf(not EXPERIMENTAL_FEATURES, "upgrade protocol not available") @@ -3837,8 +3836,8 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): # Make sure we already skip the first of these. l1.daemon.wait_for_log('billboard perm: Reconnected, and reestablished.') - assert 'option_static_remotekey' not in only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['features'] - assert 'option_static_remotekey' not in only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['features'] + assert 'option_static_remotekey' not in only_one(l1.rpc.listpeerchannels()['channels'])['features'] + assert 'option_static_remotekey' not in only_one(l2.rpc.listpeerchannels()['channels'])['features'] sleeptime = 1 while True: @@ -3858,8 +3857,8 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): l1.daemon.logsearch_start = oldstart assert l1.daemon.wait_for_log('option_static_remotekey enabled at 2/2') assert l2.daemon.wait_for_log('option_static_remotekey enabled at 2/2') - assert 'option_static_remotekey' in only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['features'] - assert 'option_static_remotekey' in only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['features'] + assert 'option_static_remotekey' in only_one(l1.rpc.listpeerchannels()['channels'])['features'] + assert 'option_static_remotekey' in only_one(l2.rpc.listpeerchannels()['channels'])['features'] @unittest.skipIf(not EXPERIMENTAL_FEATURES, "quiescence is experimental") @@ -3924,8 +3923,8 @@ def test_multichan_stress(node_factory, executor, bitcoind): bitcoind.generate_block(1) sync_blockheight(bitcoind, [l2]) l2.rpc.fundchannel(l3.info['id'], '0.01001btc') - assert(len(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) == 2) - assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2) + assert(len(l2.rpc.listpeerchannels(l3.info['id'])['channels']) == 2) + assert(len(l3.rpc.listpeerchannels(l2.info['id'])['channels']) == 2) # Make sure gossip works. bitcoind.generate_block(6, wait_for_mempool=1) @@ -4076,17 +4075,17 @@ def test_multichan(node_factory, executor, bitcoind): bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1, l2, l3]) l2.rpc.fundchannel(l3.info['id'], '0.01001btc') - assert(len(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) == 2) - assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2) + assert(len(l2.rpc.listpeerchannels(l3.info['id'])['channels']) == 2) + assert(len(l3.rpc.listpeerchannels(l2.info['id'])['channels']) == 2) bitcoind.generate_block(1, wait_for_mempool=1) sync_blockheight(bitcoind, [l1, l2, l3]) # Make sure new channel is also CHANNELD_NORMAL - wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == ["CHANNELD_NORMAL", "CHANNELD_NORMAL"]) + wait_for(lambda: [c['state'] for c in l2.rpc.listpeerchannels(l3.info['id'])['channels']] == ["CHANNELD_NORMAL", "CHANNELD_NORMAL"]) # Dance around to get the *other* scid. - wait_for(lambda: all(['short_channel_id' in c for c in l3.rpc.listpeers()['peers'][0]['channels']])) - scids = [c['short_channel_id'] for c in l3.rpc.listpeers()['peers'][0]['channels']] + wait_for(lambda: all(['short_channel_id' in c for c in l3.rpc.listpeerchannels()['channels']])) + scids = [c['short_channel_id'] for c in l3.rpc.listpeerchannels()['channels']] assert len(scids) == 2 if scids[0] == scid23a: @@ -4105,13 +4104,15 @@ def test_multichan(node_factory, executor, bitcoind): 'id': l3.info['id'], 'delay': 5, 'channel': scid23a}] - before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + + before = l2.rpc.listpeerchannels(l3.info['id'])['channels'] inv1 = l3.rpc.invoice(100000000, "invoice", "invoice") l1.rpc.sendpay(route, inv1['payment_hash'], payment_secret=inv1['payment_secret']) l1.rpc.waitsendpay(inv1['payment_hash']) + # Wait until HTLCs fully settled - wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []]) - after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + wait_for(lambda: [c['htlcs'] for c in l2.rpc.listpeerchannels(l3.info['id'])['channels']] == [[], []]) + after = l2.rpc.listpeerchannels(l3.info['id'])['channels'] if before[0]['short_channel_id'] == scid23a: chan23a_idx = 0 @@ -4130,14 +4131,14 @@ def test_multichan(node_factory, executor, bitcoind): assert before[chan23a_idx]['to_us_msat'] == after[chan23a_idx]['to_us_msat'] assert before[chan23b_idx]['to_us_msat'] != after[chan23b_idx]['to_us_msat'] - before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + before = l2.rpc.listpeerchannels(l3.info['id'])['channels'] route[1]['channel'] = scid23b inv2 = l3.rpc.invoice(100000000, "invoice2", "invoice2") l1.rpc.sendpay(route, inv2['payment_hash'], payment_secret=inv2['payment_secret']) l1.rpc.waitsendpay(inv2['payment_hash']) # Wait until HTLCs fully settled - wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []]) - after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + wait_for(lambda: [c['htlcs'] for c in l2.rpc.listpeerchannels(l3.info['id'])['channels']] == [[], []]) + after = l2.rpc.listpeerchannels(l3.info['id'])['channels'] # Now the first channel is larger! assert before[chan23a_idx]['to_us_msat'] != after[chan23a_idx]['to_us_msat'] @@ -4283,10 +4284,22 @@ def test_no_reconnect_awating_unilateral(node_factory, bitcoind): # Close immediately. l1.rpc.close(l2.info['id'], 1) - wait_for(lambda: only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['state'] == 'AWAITING_UNILATERAL') + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['state'] == 'AWAITING_UNILATERAL') # After switching to AWAITING_UNILATERAL it will *not* try to reconnect. l1.daemon.wait_for_log("State changed from CHANNELD_SHUTTING_DOWN to AWAITING_UNILATERAL") time.sleep(10) assert not l1.daemon.is_in_log('Will try reconnect', start=l1.daemon.logsearch_start) + + +def test_peer_disconnected_reflected_in_channel_state(node_factory): + """ + Make sure that if a node is disconnected we have the value correct value + across listpeer and listpeerchannels. + """ + l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True}) + l2.stop() + + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + wait_for(lambda: only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['peer_connected'] is False) diff --git a/tests/test_db.py b/tests/test_db.py index eccb492add99..0a0f8bea95d6 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -120,8 +120,8 @@ def test_max_channel_id(node_factory, bitcoind): l2.wait_for_channel_onchain(l1.info['id']) bitcoind.generate_block(101) - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) + wait_for(lambda: l1.rpc.listpeerchannels()['channels'] == []) + wait_for(lambda: l2.rpc.listpeerchannels()['channels'] == []) # Stop l2, and restart l2.stop() diff --git a/tests/test_gossip.py b/tests/test_gossip.py index d12351c146da..5793192912fd 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -739,8 +739,8 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): # Make sure l4 has received all the gossip. l4.daemon.wait_for_logs(['Received node_announcement for node ' + n.info['id'] for n in (l1, l2, l3)]) - scid12 = only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'][0]['short_channel_id'] - scid23 = only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels'][0]['short_channel_id'] + scid12 = l1.rpc.listpeerchannels(l2.info['id'])['channels'][0]['short_channel_id'] + scid23 = l3.rpc.listpeerchannels(l2.info['id'])['channels'][0]['short_channel_id'] block12 = int(scid12.split('x')[0]) block23 = int(scid23.split('x')[0]) @@ -1419,7 +1419,7 @@ def test_gossip_notices_close(node_factory, bitcoind): node_announcement = l1.daemon.is_in_log(r'\[IN\] 0101').split(' ')[-1][:-1] txid = l2.rpc.close(l3.info['id'])['txid'] - wait_for(lambda: only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'][0]['state'] == 'CLOSINGD_COMPLETE') + wait_for(lambda: l2.rpc.listpeerchannels(l3.info['id'])['channels'][0]['state'] == 'CLOSINGD_COMPLETE') bitcoind.generate_block(13, txid) wait_for(lambda: l1.rpc.listchannels()['channels'] == []) diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 5df57e843afa..cad21f6bbeec 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -170,7 +170,7 @@ def test_invoice_routeboost(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0]['short_channel_id'] + assert r['short_channel_id'] == l2.rpc.listpeerchannels(l1.info['id'])['channels'][0]['short_channel_id'] assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -233,7 +233,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Make sure channel is totally public. wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid_dummy)['channels']] == [True, True]) - alias = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['alias']['local'] + alias = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['alias']['local'] # Since there's only one route, it will reluctantly hint that even # though it's private inv = l2.rpc.invoice(amount_msat=123456, label="inv0", description="?") diff --git a/tests/test_misc.py b/tests/test_misc.py index 3feb87ccfca4..076176f3f67d 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1268,7 +1268,7 @@ def test_funding_reorg_private(node_factory, bitcoind): bitcoind.generate_block(1) # height 106 daemon = 'DUALOPEND' if l1.config('experimental-dual-fund') else 'CHANNELD' - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['status'] + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['status'] == ['{}_AWAITING_LOCKIN:Funding needs 1 more confirmations to be ready.'.format(daemon)]) bitcoind.generate_block(1) # height 107 l1.wait_channel_active('106x1x0') @@ -1325,7 +1325,7 @@ def no_more_blocks(req): bitcoind.generate_block(1) l1.daemon.wait_for_log(r'Peer transient failure .* short_channel_id changed to 104x1x0 \(was 103x1x0\)') - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'][0]['channels'])['status'] == [ + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', 'CHANNELD_NORMAL:Channel ready for use. They need our announcement signatures.']) @@ -1335,7 +1335,7 @@ def no_more_blocks(req): wait_for(lambda: chan_active(l2, '104x1x0', True)) assert l2.rpc.listchannels('103x1x0')['channels'] == [] - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'][0]['channels'])['status'] == [ + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', 'CHANNELD_NORMAL:Channel ready for use. Channel announced.']) @@ -2663,7 +2663,7 @@ def test_listforwards_and_listhtlcs(node_factory, bitcoind): # Once channels are gone, htlcs are gone. for n in (l1, l2, l3, l4): # They might reconnect, but still will have no channels - wait_for(lambda: all(p['channels'] == [] for p in n.rpc.listpeers()['peers'])) + wait_for(lambda: n.rpc.listpeerchannels()['channels'] == []) assert n.rpc.listhtlcs() == {'htlcs': []} # But forwards are not forgotten! diff --git a/tests/test_opening.py b/tests/test_opening.py index a3a69586c0fe..aa78f0e6fca4 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -13,7 +13,7 @@ def find_next_feerate(node, peer): - chan = only_one(only_one(node.rpc.listpeers(peer.info['id'])['peers'])['channels']) + chan = only_one(node.rpc.listpeerchannels(peer.info['id'])['channels']) return chan['next_feerate'] @@ -110,11 +110,8 @@ def test_multifunding_v2_best_effort(node_factory, bitcoind): # open again, so multiple channels may remain # listed. def get_funded_channel_scid(n1, n2): - peers = n1.rpc.listpeers(n2.info['id'])['peers'] - assert len(peers) == 1 - peer = peers[0] - channels = peer['channels'] - assert channels + channels = n1.rpc.listpeerchannels(n2.info['id'])['channels'] + assert channels and len(channels) != 0 for c in channels: state = c['state'] if state in ('DUALOPEND_AWAITING_LOCKIN', 'CHANNELD_AWAITING_LOCKIN', 'CHANNELD_NORMAL'): @@ -178,7 +175,7 @@ def test_v2_open_sigs_restart(node_factory, bitcoind): pass l2.daemon.wait_for_log('Broadcasting funding tx') - txid = l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0]['funding_txid'] + txid = l2.rpc.listpeerchannels(l1.info['id'])['channels'][0]['funding_txid'] bitcoind.generate_block(6, wait_for_mempool=txid) # Make sure we're ok. @@ -267,7 +264,7 @@ def test_v2_rbf_single(node_factory, bitcoind, chainparams): next_feerate = find_next_feerate(l1, l2) # Check that feerate info is correct - info_1 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) + info_1 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels']) assert info_1['initial_feerate'] == info_1['last_feerate'] rate = int(info_1['last_feerate'][:-5]) assert int(info_1['next_feerate'][:-5]) == rate * 65 // 64 @@ -286,7 +283,7 @@ def test_v2_rbf_single(node_factory, bitcoind, chainparams): assert update['commitments_secured'] # Check that feerate info has incremented - info_2 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) + info_2 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels']) assert info_1['initial_feerate'] == info_2['initial_feerate'] assert info_1['next_feerate'] == info_2['last_feerate'] @@ -301,7 +298,7 @@ def test_v2_rbf_single(node_factory, bitcoind, chainparams): l1.rpc.openchannel_signed(chan_id, signed_psbt) # Do it again, with a higher feerate - info_2 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) + info_2 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels']) assert info_1['initial_feerate'] == info_2['initial_feerate'] assert info_1['next_feerate'] == info_2['last_feerate'] rate = int(info_2['last_feerate'][:-5]) @@ -328,7 +325,7 @@ def test_v2_rbf_single(node_factory, bitcoind, chainparams): l1.daemon.wait_for_log(' to CHANNELD_NORMAL') # Check that feerate info is gone - info_1 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) + info_1 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels']) assert 'initial_feerate' not in info_1 assert 'last_feerate' not in info_1 assert 'next_feerate' not in info_1 @@ -375,7 +372,7 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): est_fees = calc_lease_fee(amount, feerate, rates) # This should be the accepter's amount - fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] + fundings = only_one(l1.rpc.listpeerchannels()['channels'])['funding'] assert Millisatoshi(amount * 1000) == fundings['remote_funds_msat'] assert Millisatoshi(est_fees + amount * 1000) == fundings['local_funds_msat'] assert Millisatoshi(est_fees) == fundings['fee_paid_msat'] @@ -416,9 +413,9 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): assert l2.rpc.listdatastore() == {'datastore': []} # This should be the accepter's amount - fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] - # The lease is still there! - assert Millisatoshi(amount * 1000) == fundings['remote_funds_msat'] + fundings = only_one(l1.rpc.listpeerchannels()['channels'])['funding'] + # The is still there! + assert Millisatoshi(amount * 1000) == Millisatoshi(fundings['remote_funds_msat']) wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(l1.get_channel_scid(l2))['channels']] == [True, True]) @@ -791,8 +788,8 @@ def test_rbf_reconnect_tx_sigs(node_factory, bitcoind, chainparams): l1.daemon.wait_for_log(' to CHANNELD_NORMAL') # Check that they have matching funding txid - l1_funding_txid = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding_txid'] - l2_funding_txid = only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['funding_txid'] + l1_funding_txid = only_one(l1.rpc.listpeerchannels()['channels'])['funding_txid'] + l2_funding_txid = only_one(l2.rpc.listpeerchannels()['channels'])['funding_txid'] assert l1_funding_txid == l2_funding_txid @@ -858,7 +855,7 @@ def test_rbf_fails_to_broadcast(node_factory, bitcoind, chainparams): # Check that we're waiting for lockin l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] assert inflights[-1]['funding_txid'] in bitcoind.rpc.getrawmempool() def run_retry(): @@ -885,7 +882,7 @@ def run_retry(): signed_psbt = run_retry() l1.rpc.openchannel_signed(chan_id, signed_psbt) - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] assert inflights[-1]['funding_txid'] in bitcoind.rpc.getrawmempool() # Restart and listpeers, used to crash @@ -895,7 +892,7 @@ def run_retry(): # We've restarted. Let's RBF signed_psbt = run_retry() l1.rpc.openchannel_signed(chan_id, signed_psbt) - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] assert len(inflights) == 3 assert inflights[-1]['funding_txid'] in bitcoind.rpc.getrawmempool() @@ -903,7 +900,7 @@ def run_retry(): # Are inflights the same post restart prev_inflights = inflights - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] assert prev_inflights == inflights assert inflights[-1]['funding_txid'] in bitcoind.rpc.getrawmempool() @@ -942,7 +939,7 @@ def test_rbf_broadcast_close_inflights(node_factory, bitcoind, chainparams): # Check that we're waiting for lockin l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] assert inflights[-1]['funding_txid'] in bitcoind.rpc.getrawmempool() # Make it such that l1 and l2 cannot broadcast transactions @@ -970,10 +967,10 @@ def run_retry(): signed_psbt = run_retry() l1.rpc.openchannel_signed(chan_id, signed_psbt) - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] assert inflights[-1]['funding_txid'] not in bitcoind.rpc.getrawmempool() - cmtmt_txid = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['scratch_txid'] + cmtmt_txid = only_one(l1.rpc.listpeerchannels()['channels'])['scratch_txid'] assert cmtmt_txid == inflights[-1]['scratch_txid'] # l2 goes offline @@ -1016,7 +1013,7 @@ def test_rbf_non_last_mined(node_factory, bitcoind, chainparams): # Check that we're waiting for lockin l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] assert inflights[-1]['funding_txid'] in bitcoind.rpc.getrawmempool() def run_retry(): @@ -1056,7 +1053,7 @@ def censoring_sendrawtx(r): l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', None) # We fetch out our inflights list - inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + inflights = only_one(l1.rpc.listpeerchannels()['channels'])['inflight'] # l2 goes offline l2.stop() @@ -1071,7 +1068,7 @@ def censoring_sendrawtx(r): l1.daemon.wait_for_log(r'to CHANNELD_NORMAL') l2.daemon.wait_for_log(r'to CHANNELD_NORMAL') - channel = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) + channel = only_one(l1.rpc.listpeerchannels()['channels']) assert channel['funding_txid'] == inflights[1]['funding_txid'] assert channel['scratch_txid'] == inflights[1]['scratch_txid'] @@ -1113,7 +1110,7 @@ def test_funder_options(node_factory, bitcoind): # l2 funds a chanenl with us. We don't contribute l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l2.fundchannel(l1, 10**6) - chan_info = only_one(only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']) + chan_info = only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels']) # l1 contributed nothing assert chan_info['funding']['remote_funds_msat'] == Millisatoshi('0msat') assert chan_info['funding']['local_funds_msat'] != Millisatoshi('0msat') @@ -1146,7 +1143,7 @@ def test_funder_options(node_factory, bitcoind): {'fund_probability': 100}) l3.rpc.connect(l1.info['id'], 'localhost', l1.port) l3.fundchannel(l1, 10**6) - chan_info = only_one(only_one(l3.rpc.listpeers(l1.info['id'])['peers'])['channels']) + chan_info = only_one(l3.rpc.listpeerchannels(l1.info['id'])['channels']) # l1 contributed all its funds! assert chan_info['funding']['remote_funds_msat'] == Millisatoshi('9994255000msat') assert chan_info['funding']['local_funds_msat'] == Millisatoshi('1000000000msat') @@ -1291,8 +1288,8 @@ def test_zeroconf_mindepth(bitcoind, node_factory): bitcoind.generate_block(4) # Confirm on the l2 side. l1.daemon.wait_for_log(r'peer_out WIRE_CHANNEL_READY') - wait_for(lambda: l1.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") - wait_for(lambda: l2.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == "CHANNELD_NORMAL") + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == "CHANNELD_NORMAL") def test_zeroconf_open(bitcoind, node_factory): @@ -1335,8 +1332,8 @@ def test_zeroconf_open(bitcoind, node_factory): r'Peer told us that they\'ll use alias=[0-9x]+ for this channel', ]) - wait_for(lambda: l1.rpc.listpeers()['peers'][0]['channels'][0]['state'] == 'CHANNELD_NORMAL') - wait_for(lambda: l2.rpc.listpeers()['peers'][0]['channels'][0]['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL') wait_for(lambda: l2.rpc.listincoming()['incoming'] != []) inv = l2.rpc.invoice(10**8, 'lbl', 'desc')['bolt11'] @@ -1344,7 +1341,7 @@ def test_zeroconf_open(bitcoind, node_factory): pprint(details) assert('routes' in details and len(details['routes']) == 1) hop = details['routes'][0][0] # First (and only) hop of hint 0 - l1alias = l1.rpc.listpeers()['peers'][0]['channels'][0]['alias']['local'] + l1alias = only_one(l1.rpc.listpeerchannels()['channels'])['alias']['local'] assert(hop['pubkey'] == l1.info['id']) # l1 is the entrypoint assert(hop['short_channel_id'] == l1alias) # Alias has to make sense to entrypoint l1.rpc.pay(inv) @@ -1389,8 +1386,8 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): l1.daemon.wait_for_log(r'Got WIRE_HSMD_CUPDATE_SIG_REQ') l2.daemon.wait_for_log(r'Got WIRE_HSMD_CUPDATE_SIG_REQ') - l1chan = l1.rpc.listpeers()['peers'][0]['channels'][0] - l2chan = l2.rpc.listpeers()['peers'][0]['channels'][0] + l1chan = only_one(l1.rpc.listpeerchannels()['channels']) + l2chan = only_one(l2.rpc.listpeerchannels()['channels']) channel_id = l1chan['channel_id'] # We have no confirmation yet, so no `short_channel_id` @@ -1421,8 +1418,8 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): l1.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') l2.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') - l1chan = l1.rpc.listpeers()['peers'][0]['channels'][0] - l2chan = l2.rpc.listpeers()['peers'][0]['channels'][0] + l1chan = only_one(l1.rpc.listpeerchannels()['channels']) + l2chan = only_one(l2.rpc.listpeerchannels()['channels']) assert('short_channel_id' in l1chan) assert('short_channel_id' in l2chan) @@ -1501,7 +1498,7 @@ def test_zeroconf_forward(node_factory, bitcoind): wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4) # Make sure all htlcs completely settled! - wait_for(lambda: all(only_one(p['channels'])['htlcs'] == [] for p in l2.rpc.listpeers()['peers'])) + wait_for(lambda: (p['htlcs'] == [] for p in l2.rpc.listpeerchannels()['channels'])) inv = l1.rpc.invoice(42, 'back1', 'desc')['bolt11'] l3.rpc.pay(inv) @@ -1653,9 +1650,9 @@ def test_scid_alias_private(node_factory, bitcoind): l2.rpc.fundchannel(l3.info['id'], 'all', announce=False) bitcoind.generate_block(1, wait_for_mempool=1) - wait_for(lambda: only_one(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'])['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL') - chan = only_one(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) + chan = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels']) assert chan['private'] is True scid23 = chan['short_channel_id'] alias23 = chan['alias']['local'] @@ -1667,7 +1664,7 @@ def test_scid_alias_private(node_factory, bitcoind): bitcoind.generate_block(6, wait_for_mempool=1) wait_for(lambda: len(l3.rpc.listchannels(source=l1.info['id'])['channels']) == 1) - chan = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) + chan = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels']) assert chan['private'] is False scid12 = chan['short_channel_id'] @@ -1743,7 +1740,7 @@ def test_zeroconf_multichan_forward(node_factory): inv = l3.rpc.invoice(amount_msat=10000, label='lbl1', description='desc')['bolt11'] l1.rpc.pay(inv) - for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']: + for c in l2.rpc.listpeerchannels(l3.info['id'])['channels']: if c['channel_id'] == zeroconf_cid: zeroconf_scid = c['alias']['local'] else: @@ -1796,12 +1793,12 @@ def test_zeroreserve(node_factory, bitcoind): wait_for(lambda: l3.channel_state(l1) == 'CHANNELD_NORMAL') # Now make sure we all agree on each others reserves - l1c1 = l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'][0] - l2c1 = l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0] - l2c2 = l2.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0] - l3c2 = l3.rpc.listpeers(l2.info['id'])['peers'][0]['channels'][0] - l3c3 = l3.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0] - l1c3 = l1.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0] + l1c1 = l1.rpc.listpeerchannels(l2.info['id'])['channels'][0] + l2c1 = l2.rpc.listpeerchannels(l1.info['id'])['channels'][0] + l2c2 = l2.rpc.listpeerchannels(l3.info['id'])['channels'][0] + l3c2 = l3.rpc.listpeerchannels(l2.info['id'])['channels'][0] + l3c3 = l3.rpc.listpeerchannels(l1.info['id'])['channels'][0] + l1c3 = l1.rpc.listpeerchannels(l3.info['id'])['channels'][0] # l1 imposed a 0sat reserve on l2, while l2 imposed the default 1% reserve on l1 assert l1c1['their_reserve_msat'] == l2c1['our_reserve_msat'] == Millisatoshi('0sat') @@ -1821,7 +1818,7 @@ def test_zeroreserve(node_factory, bitcoind): l2.drain(l1) # Remember that this is the reserve l1 imposed on l2, so l2 can drain completely - l2c1 = l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0] + l2c1 = l2.rpc.listpeerchannels(l1.info['id'])['channels'][0] # And despite us briefly being above dust (with a to_us output), # closing should result in the output being trimmed again since we diff --git a/tests/test_pay.py b/tests/test_pay.py index aa5d71848147..1d9686844cc8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -267,7 +267,7 @@ def test_pay_disconnect(node_factory, bitcoind): l2.stop() # Make sure channeld has exited! - wait_for(lambda: 'owner' not in only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])) + wait_for(lambda: 'owner' not in only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])) # Can't pay while its offline. with pytest.raises(RpcError, match=r'failed: WIRE_TEMPORARY_CHANNEL_FAILURE \(First peer not ready\)'): @@ -622,12 +622,12 @@ def invoice_unpaid(dst, label): assert invoice_unpaid(l2, 'testpayment2') # FIXME: test paying via another node, should fail to pay twice. - p1 = l1.rpc.getpeer(l2.info['id'], 'info') - p2 = l2.rpc.getpeer(l1.info['id'], 'info') - assert only_one(p1['channels'])['to_us_msat'] == 10**6 * 1000 - assert only_one(p1['channels'])['total_msat'] == 10**6 * 1000 - assert only_one(p2['channels'])['to_us_msat'] == 0 - assert only_one(p2['channels'])['total_msat'] == 10**6 * 1000 + c1 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels']) + c2 = only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels']) + assert c1['to_us_msat'] == 10**6 * 1000 + assert c1['total_msat'] == 10**6 * 1000 + assert c2['to_us_msat'] == 0 + assert c2['total_msat'] == 10**6 * 1000 # This works. before = int(time.time()) @@ -648,13 +648,13 @@ def invoice_unpaid(dst, label): # Balances should reflect it. def check_balances(): - p1 = l1.rpc.getpeer(l2.info['id'], 'info') - p2 = l2.rpc.getpeer(l1.info['id'], 'info') + c1 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels']) + c2 = only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels']) return ( - only_one(p1['channels'])['to_us_msat'] == 10**6 * 1000 - amt - and only_one(p1['channels'])['total_msat'] == 10**6 * 1000 - and only_one(p2['channels'])['to_us_msat'] == amt - and only_one(p2['channels'])['total_msat'] == 10**6 * 1000 + c1['to_us_msat'] == 10**6 * 1000 - amt + and c1['total_msat'] == 10**6 * 1000 + and c2['to_us_msat'] == amt + and c2['total_msat'] == 10**6 * 1000 ) wait_for(check_balances) @@ -1079,10 +1079,10 @@ def test_forward(node_factory, bitcoind): # If they're at different block heights we can get spurious errors. sync_blockheight(bitcoind, [l1, l2, l3]) - chanid1 = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] - chanid2 = only_one(l2.rpc.getpeer(l3.info['id'])['channels'])['short_channel_id'] - assert only_one(l2.rpc.getpeer(l1.info['id'])['channels'])['short_channel_id'] == chanid1 - assert only_one(l3.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] == chanid2 + chanid1 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['short_channel_id'] + chanid2 = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['short_channel_id'] + assert only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['short_channel_id'] == chanid1 + assert only_one(l3.rpc.listpeerchannels(l2.info['id'])['channels'])['short_channel_id'] == chanid2 inv = l3.rpc.invoice(100000000, 'testpayment1', 'desc') rhash = inv['payment_hash'] @@ -1396,8 +1396,8 @@ def test_forward_stats(node_factory, bitcoind): states = [f['state'] for f in forwardings] assert(states == [1, 2, 0]) # settled, failed, offered - inchan = l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0] - outchan = l2.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0] + inchan = l2.rpc.listpeerchannels(l1.info['id'])['channels'][0] + outchan = l2.rpc.listpeerchannels(l3.info['id'])['channels'][0] # Check that we correctly account channel changes assert inchan['in_payments_offered'] == 3 @@ -1739,8 +1739,7 @@ def test_pay_retry(node_factory, bitcoind, executor, chainparams): def exhaust_channel(opener, peer, scid, already_spent=0): """Spend all available capacity (10^6 - 1%) of channel """ - peer_node = opener.rpc.listpeers(peer.info['id'])['peers'][0] - chan = peer_node['channels'][0] + chan = only_one(opener.rpc.listpeerchannels(peer.info['id'])["channels"]) maxpay = chan['spendable_msat'] lbl = ''.join(random.choice(string.ascii_letters) for _ in range(20)) inv = peer.rpc.invoice(maxpay, lbl, "exhaust_channel") @@ -1863,7 +1862,7 @@ def test_pay_routeboost(node_factory, bitcoind): assert 'routehint_modifications' not in only_one(status['pay']) assert 'local_exclusions' not in only_one(status['pay']) attempts = only_one(status['pay'])['attempts'] - scid34 = only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['channels'][0]['alias']['local'] + scid34 = l3.rpc.listpeerchannels(l4.info['id'])['channels'][0]['alias']['local'] assert(len(attempts) == 1) a = attempts[0] assert(a['strategy'] == "Initial attempt") @@ -1872,7 +1871,7 @@ def test_pay_routeboost(node_factory, bitcoind): # With dev-route option we can test longer routehints. if DEVELOPER: - scid45 = only_one(l4.rpc.listpeers(l5.info['id'])['peers'])['channels'][0]['alias']['local'] + scid45 = l4.rpc.listpeerchannels(l5.info['id'])['channels'][0]['alias']['local'] routel3l4l5 = [{'id': l3.info['id'], 'short_channel_id': scid34, 'fee_base_msat': 1000, @@ -1970,10 +1969,10 @@ def channel_get_config(scid): # This will be the capacity - reserves: assert(db_fees[0]['htlc_maximum_msat'] == MAX_HTLC) # this is also what listpeers should return - peers = l1.rpc.listpeers()['peers'] - assert peers[0]['channels'][0]['fee_base_msat'] == DEF_BASE_MSAT - assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM - assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == MAX_HTLC + channel = only_one(l1.rpc.listpeerchannels()['channels']) + assert channel['fee_base_msat'] == DEF_BASE_MSAT + assert channel['fee_proportional_millionths'] == DEF_PPM + assert channel['maximum_htlc_out_msat'] == MAX_HTLC # custom setchannel scid result = l1.rpc.setchannel(scid, 1337, 137, 17, 133337) @@ -1995,11 +1994,11 @@ def channel_get_config(scid): assert(db_fees[0]['htlc_minimum_msat'] == 17) assert(db_fees[0]['htlc_maximum_msat'] == 133337) # also check for updated values in `listpeers` - peers = l1.rpc.listpeers()['peers'] - assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(1337) - assert peers[0]['channels'][0]['fee_proportional_millionths'] == 137 - assert peers[0]['channels'][0]['minimum_htlc_out_msat'] == 17 - assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == 133337 + channel = only_one(l1.rpc.listpeerchannels()['channels']) + assert channel['fee_base_msat'] == Millisatoshi(1337) + assert channel['fee_proportional_millionths'] == 137 + assert channel['minimum_htlc_out_msat'] == 17 + assert channel['maximum_htlc_out_msat'] == 133337 # wait for gossip and check if l1 sees new fees in listchannels wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, 1337]) @@ -2068,9 +2067,9 @@ def channel_get_config(scid): assert(db_fees[0]['feerate_base'] == 0) assert(db_fees[0]['feerate_ppm'] == 0) # also check for updated values in `listpeers` - peers = l1.rpc.listpeers()['peers'] - assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(0) - assert peers[0]['channels'][0]['fee_proportional_millionths'] == 0 + channel = only_one(l1.rpc.listpeerchannels()['channels']) + assert channel['fee_base_msat'] == Millisatoshi(0) + assert channel['fee_proportional_millionths'] == 0 # check also peer id can be used result = l1.rpc.setchannel(l2.info['id'], 142, 143) @@ -2463,7 +2462,7 @@ def test_channel_spendable(node_factory, bitcoind): payment_hash = inv['payment_hash'] # We should be able to spend this much, and not one msat more! - amount = l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] + amount = l1.rpc.listpeerchannels()['channels'][0]['spendable_msat'] route = l1.rpc.getroute(l2.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) @@ -2477,16 +2476,16 @@ def test_channel_spendable(node_factory, bitcoind): # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. - wait_for(lambda: len(l1.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 1) - assert l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] == Millisatoshi(0) + wait_for(lambda: len(l1.rpc.listpeerchannels()['channels'][0]['htlcs']) == 1) + assert l1.rpc.listpeerchannels()['channels'][0]['spendable_msat'] == Millisatoshi(0) l1.rpc.waitsendpay(payment_hash, TIMEOUT) # Make sure l2 thinks it's all over. - wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) + wait_for(lambda: len(l2.rpc.listpeerchannels()['channels'][0]['htlcs']) == 0) # Now, reverse should work similarly. inv = l1.rpc.invoice('any', 'inv', 'for testing') payment_hash = inv['payment_hash'] - amount = l2.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] + amount = l2.rpc.listpeerchannels()['channels'][0]['spendable_msat'] # Turns out we won't route this, as it's over max - reserve: route = l2.rpc.getroute(l1.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] @@ -2502,8 +2501,8 @@ def test_channel_spendable(node_factory, bitcoind): # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. - wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 1) - assert l2.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] == Millisatoshi(0) + wait_for(lambda: len(l2.rpc.listpeerchannels()['channels'][0]['htlcs']) == 1) + assert l2.rpc.listpeerchannels()['channels'][0]['spendable_msat'] == Millisatoshi(0) l2.rpc.waitsendpay(payment_hash, TIMEOUT) @@ -2518,7 +2517,7 @@ def test_channel_receivable(node_factory, bitcoind): payment_hash = inv['payment_hash'] # We should be able to receive this much, and not one msat more! - amount = l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] + amount = l2.rpc.listpeerchannels()['channels'][0]['receivable_msat'] route = l1.rpc.getroute(l2.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) @@ -2532,17 +2531,17 @@ def test_channel_receivable(node_factory, bitcoind): # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. - wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 1) - assert l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] == Millisatoshi(0) + wait_for(lambda: len(l2.rpc.listpeerchannels()['channels'][0]['htlcs']) == 1) + assert l2.rpc.listpeerchannels()['channels'][0]['receivable_msat'] == Millisatoshi(0) l1.rpc.waitsendpay(payment_hash, TIMEOUT) # Make sure both think it's all over. - wait_for(lambda: len(l1.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) - wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) + wait_for(lambda: len(l1.rpc.listpeerchannels()['channels'][0]['htlcs']) == 0) + wait_for(lambda: len(l2.rpc.listpeerchannels()['channels'][0]['htlcs']) == 0) # Now, reverse should work similarly. inv = l1.rpc.invoice('any', 'inv', 'for testing') payment_hash = inv['payment_hash'] - amount = l1.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] + amount = l1.rpc.listpeerchannels()['channels'][0]['receivable_msat'] # Turns out we won't route this, as it's over max - reserve: route = l2.rpc.getroute(l1.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] @@ -2558,8 +2557,8 @@ def test_channel_receivable(node_factory, bitcoind): # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. - wait_for(lambda: len(l1.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 1) - assert l1.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] == Millisatoshi(0) + wait_for(lambda: len(l1.rpc.listpeerchannels()['channels'][0]['htlcs']) == 1) + assert l1.rpc.listpeerchannels()['channels'][0]['receivable_msat'] == Millisatoshi(0) l2.rpc.waitsendpay(payment_hash, TIMEOUT) @@ -2582,10 +2581,10 @@ def test_channel_spendable_large(node_factory, bitcoind): payment_hash = inv['payment_hash'] # We should be able to spend this much, and not one msat more! - spendable = l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] + spendable = l1.rpc.listpeerchannels()['channels'][0]['spendable_msat'] # receivable from the other side should calculate to the exact same amount - receivable = l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] + receivable = l2.rpc.listpeerchannels()['channels'][0]['receivable_msat'] assert spendable == receivable # route or waitsendpay fill fail. @@ -2604,8 +2603,8 @@ def test_channel_spendable_receivable_capped(node_factory, bitcoind): """Test that spendable_msat and receivable_msat is capped at 2^32-1""" sats = 16777215 l1, l2 = node_factory.line_graph(2, fundamount=sats, wait_for_announce=False) - assert l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] == Millisatoshi(0xFFFFFFFF) - assert l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] == Millisatoshi(0xFFFFFFFF) + assert l1.rpc.listpeerchannels()['channels'][0]['spendable_msat'] == Millisatoshi(0xFFFFFFFF) + assert l2.rpc.listpeerchannels()['channels'][0]['receivable_msat'] == Millisatoshi(0xFFFFFFFF) @unittest.skipIf(True, "Test is extremely flaky") @@ -3647,7 +3646,7 @@ def test_keysend_routehint(node_factory): routehints = [ [ { - 'scid': l3.rpc.listpeers()['peers'][0]['channels'][0]['alias']['remote'], + 'scid': only_one(l3.rpc.listpeerchannels()['channels'])['alias']['remote'], 'id': l2.info['id'], 'feebase': '1msat', 'feeprop': 10, @@ -3767,8 +3766,7 @@ def test_pay_peer(node_factory, bitcoind): wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 6) def spendable(n1, n2): - peer = n1.rpc.listpeers(n2.info['id'])['peers'][0] - chan = peer['channels'][0] + chan = n1.rpc.listpeerchannels(n2.info['id'])['channels'][0] avail = chan['spendable_msat'] return avail @@ -3876,8 +3874,8 @@ def test_mpp_adaptive(node_factory, bitcoind): l1.rpc.listpeers() # Make sure neither channel can fit the payment by itself. - c12 = l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'][0] - c34 = l3.rpc.listpeers(l4.info['id'])['peers'][0]['channels'][0] + c12 = l1.rpc.listpeerchannels(l2.info['id'])['channels'][0] + c34 = l3.rpc.listpeerchannels(l4.info['id'])['channels'][0] assert(c12['spendable_msat'].millisatoshis < amt) assert(c34['spendable_msat'].millisatoshis < amt) @@ -3885,7 +3883,7 @@ def test_mpp_adaptive(node_factory, bitcoind): def all_htlcs(n): htlcs = [] for p in n.rpc.listpeers()['peers']: - for c in p['channels']: + for c in n.rpc.listpeerchannels(p['id'])['channels']: htlcs += c['htlcs'] return htlcs @@ -3953,7 +3951,7 @@ def test_pay_fail_unconfirmed_channel(node_factory, bitcoind): l2.rpc.pay(invl1) # Wait for us to recognize that the channel is available - wait_for(lambda: l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'].millisatoshis > amount_sat * 1000) + wait_for(lambda: l1.rpc.listpeerchannels()['channels'][0]['spendable_msat'].millisatoshis > amount_sat * 1000) # Now l1 can pay to l2. l1.rpc.pay(invl2) @@ -3974,7 +3972,7 @@ def test_bolt11_null_after_pay(node_factory, bitcoind): # Let the channel confirm. bitcoind.generate_block(6) sync_blockheight(bitcoind, [l1, l2]) - wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL') amt = Millisatoshi(amount_sat * 2 * 1000) invl1 = l1.rpc.invoice(amt, 'j', 'j')['bolt11'] @@ -4820,7 +4818,7 @@ def test_fetchinvoice_autoconnect(node_factory, bitcoind): l3.rpc.pay(l2.rpc.invoice(FUNDAMOUNT * 500, 'balancer', 'balancer')['bolt11']) # Make sure l2 has capacity (can be still resolving!). - wait_for(lambda: only_one(only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels'])['spendable_msat'] != Millisatoshi(0)) + wait_for(lambda: only_one(l2.rpc.listpeerchannels(l1.info['id'])['channels'])['spendable_msat'] != Millisatoshi(0)) l3.rpc.disconnect(l2.info['id']) l3.rpc.call('sendinvoice', {'invreq': invreq['bolt12'], 'label': 'payme for real!'}) @@ -5018,7 +5016,7 @@ def test_routehint_tous(node_factory, bitcoind): inv = l3.rpc.invoice(10, "test", "test")['bolt11'] decoded = l3.rpc.decodepay(inv) assert(only_one(only_one(decoded['routes']))['short_channel_id'] - == only_one(only_one(l3.rpc.listpeers()['peers'])['channels'])['alias']['remote']) + == only_one(l3.rpc.listpeerchannels()['channels'])['alias']['remote']) l3.stop() with pytest.raises(RpcError, match=r'Destination .* is not reachable directly and all routehints were unusable'): @@ -5043,8 +5041,8 @@ def test_setchannel_enforcement_delay(node_factory, bitcoind): opts={'fee-base': 1, 'fee-per-satoshi': 10000}) - chanid1 = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] - chanid2 = only_one(l2.rpc.getpeer(l3.info['id'])['channels'])['short_channel_id'] + chanid1 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['short_channel_id'] + chanid2 = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['short_channel_id'] route = [{'amount_msat': 1011, 'id': l2.info['id'], @@ -5159,7 +5157,7 @@ def test_sendpay_grouping(node_factory, bitcoind): assert(invoices[0]['status'] == 'unpaid') # Will reconnect automatically wait_for(lambda: only_one(l3.rpc.listpeers()['peers'])['connected'] is True) - scid = l3.rpc.listpeers()['peers'][0]['channels'][0]['short_channel_id'] + scid = l3.rpc.listpeerchannels()['channels'][0]['short_channel_id'] wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid)['channels']] == [True, True]) l1.rpc.pay(inv, amount_msat='420000msat') @@ -5174,8 +5172,8 @@ def test_pay_manual_exclude(node_factory, bitcoind): l1_id = l1.rpc.getinfo()['id'] l2_id = l2.rpc.getinfo()['id'] l3_id = l3.rpc.getinfo()['id'] - chan12 = l1.rpc.listpeers(l2_id)['peers'][0]['channels'][0] - chan23 = l2.rpc.listpeers(l3_id)['peers'][0]['channels'][0] + chan12 = l1.rpc.listpeerchannels(l2_id)['channels'][0] + chan23 = l2.rpc.listpeerchannels(l3_id)['channels'][0] scid12 = chan12['short_channel_id'] + '/' + str(chan12['direction']) scid23 = chan23['short_channel_id'] + '/' + str(chan23['direction']) inv = l3.rpc.invoice(amount_msat=123000, label='label1', description='desc')['bolt11'] @@ -5231,8 +5229,8 @@ def test_pay_middle_fail(node_factory, bitcoind, executor): {'feerates': (1500,) * 4, 'disconnect': ['-WIRE_REVOKE_AND_ACK*2']}]) - chanid12 = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] - chanid23 = only_one(l2.rpc.getpeer(l3.info['id'])['channels'])['short_channel_id'] + chanid12 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['short_channel_id'] + chanid23 = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['short_channel_id'] # Make a failing payment. route = [{'amount_msat': 1011, @@ -5253,7 +5251,7 @@ def test_pay_middle_fail(node_factory, bitcoind, executor): # l2 will go onchain since HTLC is not resolved. bitcoind.generate_block(12) sync_blockheight(bitcoind, [l1, l2, l3]) - wait_for(lambda: only_one(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'])['state'] == 'AWAITING_UNILATERAL') + wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'AWAITING_UNILATERAL') # Three blocks and it will resolve the parent. bitcoind.generate_block(3, wait_for_mempool=1) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 7f4298cec109..1e696bdc15ec 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -680,7 +680,7 @@ def test_openchannel_hook(node_factory, bitcoind): # Close it. txid = l1.rpc.close(l2.info['id'])['txid'] bitcoind.generate_block(1, txid) - wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']] == ['ONCHAIN']) + wait_for(lambda: [c['state'] for c in l1.rpc.listpeerchannels(l2.info['id'])['channels']] == ['ONCHAIN']) # Odd amount: fails l1.connect(l2) @@ -773,11 +773,11 @@ def wait_for_event(node): return event # check channel 'opener' and 'closer' within this testcase ... - assert(l1.rpc.listpeers()['peers'][0]['channels'][0]['opener'] == 'local') - assert(l2.rpc.listpeers()['peers'][0]['channels'][0]['opener'] == 'remote') + assert(l1.rpc.listpeerchannels()['channels'][0]['opener'] == 'local') + assert(l2.rpc.listpeerchannels()['channels'][0]['opener'] == 'remote') # the 'closer' should be missing initially - assert 'closer' not in l1.rpc.listpeers()['peers'][0]['channels'][0] - assert 'closer' not in l2.rpc.listpeers()['peers'][0]['channels'][0] + assert 'closer' not in l1.rpc.listpeerchannels()['channels'][0] + assert 'closer' not in l2.rpc.listpeerchannels()['channels'][0] event1 = wait_for_event(l1) event2 = wait_for_event(l2) @@ -841,8 +841,8 @@ def wait_for_event(node): assert(event2['message'] == "Peer closes channel") # 'closer' should now be set accordingly ... - assert(l1.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'local') - assert(l2.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'remote') + assert(l1.rpc.listpeerchannels()['channels'][0]['closer'] == 'local') + assert(l2.rpc.listpeerchannels()['channels'][0]['closer'] == 'remote') event1 = wait_for_event(l1) assert(event1['old_state'] == "CHANNELD_SHUTTING_DOWN") @@ -959,7 +959,7 @@ def wait_for_event(node): l1.restart() wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 1) # check 'closer' on l2 while the peer is not yet forgotten - assert(l2.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'local') + assert(l2.rpc.listpeerchannels()['channels'][0]['closer'] == 'local') if EXPERIMENTAL_DUAL_FUND: l1.daemon.wait_for_log(r'Peer has reconnected, state') l2.daemon.wait_for_log(r'Telling connectd to send error') @@ -968,7 +968,7 @@ def wait_for_event(node): # FIXME: l2 should re-xmit shutdown, but it doesn't until it's mined :( event1 = wait_for_event(l1) # Doesn't have closer, since it blames the "protocol"? - assert 'closer' not in l1.rpc.listpeers()['peers'][0]['channels'][0] + assert 'closer' not in l1.rpc.listpeerchannels()['channels'][0] assert(event1['old_state'] == "CHANNELD_NORMAL") assert(event1['new_state'] == "AWAITING_UNILATERAL") assert(event1['cause'] == "protocol") @@ -990,7 +990,7 @@ def wait_for_event(node): # Check 'closer' on l1 while the peer is not yet forgotten event1 = wait_for_event(l1) - assert(l1.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'remote') + assert(l1.rpc.listpeerchannels()['channels'][0]['closer'] == 'remote') assert(event1['old_state'] == "AWAITING_UNILATERAL") assert(event1['new_state'] == "FUNDING_SPEND_SEEN") @@ -1014,7 +1014,7 @@ def test_channel_state_change_history(node_factory, bitcoind): scid = l1.get_channel_scid(l2) l1.rpc.close(scid) - history = l1.rpc.listpeers()['peers'][0]['channels'][0]['state_changes'] + history = l1.rpc.listpeerchannels()['channels'][0]['state_changes'] if l1.config('experimental-dual-fund'): assert(history[0]['cause'] == "user") assert(history[0]['old_state'] == "DUALOPEND_OPEN_INIT") @@ -1121,8 +1121,8 @@ def test_htlc_accepted_hook_direct_restart(node_factory, executor): # Check that the status mentions the HTLC being held l2.rpc.listpeers() - peers = l2.rpc.listpeers()['peers'] - htlc_status = peers[0]['channels'][0]['htlcs'][0].get('status', None) + channel = only_one(l2.rpc.listpeerchannels()['channels']) + htlc_status = channel['htlcs'][0].get('status', None) assert htlc_status == "Waiting for the htlc_accepted hook of plugin hold_htlcs.py" needle = l2.daemon.logsearch_start @@ -1892,7 +1892,7 @@ def test_watchtower(node_factory, bitcoind, directory, chainparams): 2, opts=[{'may_fail': True, 'allow_broken_log': True}, {'plugin': p}] ) - channel_id = l1.rpc.listpeers()['peers'][0]['channels'][0]['channel_id'] + channel_id = l1.rpc.listpeerchannels()['channels'][0]['channel_id'] # Force a new commitment l1.rpc.pay(l2.rpc.invoice(25000000, 'lbl1', 'desc1')['bolt11']) @@ -2051,7 +2051,7 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): # restart to test index l2.restart() - wait_for(lambda: all(p['channels'][0]['state'] == 'CHANNELD_NORMAL' for p in l2.rpc.listpeers()['peers'])) + wait_for(lambda: all(c['state'] == 'CHANNELD_NORMAL' for c in l2.rpc.listpeerchannels()["channels"])) # close the channels down chan1 = l2.get_channel_scid(l1) @@ -2412,15 +2412,15 @@ def test_htlc_accepted_hook_fwdto(node_factory): # Add some balance l1.rpc.pay(l2.rpc.invoice(10**9 // 2, 'balance', '')['bolt11']) - wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['htlcs'] == []) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) # make it forward back down same channel. - l2.rpc.setfwdto(only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['channel_id']) + l2.rpc.setfwdto(only_one(l1.rpc.listpeerchannels()['channels'])['channel_id']) inv = l3.rpc.invoice(42, 'fwdto', '')['bolt11'] with pytest.raises(RpcError, match="WIRE_INVALID_ONION_HMAC"): l1.rpc.pay(inv) - assert l2.rpc.listforwards()['forwards'][0]['out_channel'] == only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['short_channel_id'] + assert l2.rpc.listforwards()['forwards'][0]['out_channel'] == only_one(l1.rpc.listpeerchannels()['channels'])['short_channel_id'] def test_dynamic_args(node_factory): diff --git a/tests/test_wallet.py b/tests/test_wallet.py index be6275a17d23..b6791a75b00f 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1033,9 +1033,9 @@ def test_transaction_annotations(node_factory, bitcoind): assert(types[changeidx] == 'deposit' and types[fundidx] == 'channel_funding') # And check the channel annotation on the funding output - peers = l1.rpc.listpeers()['peers'] - assert(len(peers) == 1 and len(peers[0]['channels']) == 1) - scid = peers[0]['channels'][0]['short_channel_id'] + channels = l1.rpc.listpeerchannels()['channels'] + assert(len(channels) == 1) + scid = channels[0]['short_channel_id'] assert(txs[1]['outputs'][fundidx]['channel'] == scid) From 9e8a8dbcd9e419de59269546d8c08153ed2b258a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:55:58 +1030 Subject: [PATCH 12/14] pytest: fix race in test_bookkeeping_closing_subsat_htlcs With the next change (which, as a side-effect, speeds up listpeers), we seem to hit a race in this test. The bookkeeper doesn't get to process the final payment before the node is shutdown. Signed-off-by: Rusty Russell --- tests/test_bookkeeper.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index ed31c27e91bf..9093f58df8dd 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -81,6 +81,9 @@ def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): l1.pay(l2, 222) l1.pay(l2, 4000000) + # Make sure l2 bookkeeper processes event before we stop it! + wait_for(lambda: len([e for e in l2.rpc.bkpr_listaccountevents()['events'] if e['tag'] == 'invoice']) == 3) + l2.stop() l1.rpc.close(l2.info['id'], 1) bitcoind.generate_block(5, wait_for_mempool=1) From 2dfd4a888311e21d1d3f0af73b3b9689b11f3fc0 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 12 Jan 2023 11:55:58 +1030 Subject: [PATCH 13/14] lightningd: deprecate listpeers.channels Changelog-Deprecated: JSON-RPC: `listpeers` `channels` array: use `listpeerchannels` Signed-off-by: Vincenzo Palazzo --- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 5 ++-- doc/lightning-listpeers.7.md | 46 +++++++++++++++---------------- doc/schemas/listpeers.schema.json | 4 +-- lightningd/peer_control.c | 20 ++++++++------ 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index da276661b9d7..c8096ae28a3d 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -216,7 +216,7 @@ impl From for pb::ListpeersPeers { id: c.id.serialize().to_vec(), // Rule #2 for type pubkey connected: c.connected, // Rule #2 for type boolean log: c.log.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - channels: c.channels.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannels + channels: c.channels.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 netaddr: c.netaddr.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 remote_addr: c.remote_addr, // Rule #2 for type string? features: c.features.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 01759b177ebd..9d8150536759 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1808,8 +1808,9 @@ pub mod responses { pub connected: bool, #[serde(alias = "log", skip_serializing_if = "crate::is_none_or_empty")] pub log: Option>, - #[serde(alias = "channels")] - pub channels: Vec, + #[deprecated] + #[serde(alias = "channels", skip_serializing_if = "crate::is_none_or_empty")] + pub channels: Option>, #[serde(alias = "netaddr", skip_serializing_if = "crate::is_none_or_empty")] pub netaddr: Option>, #[serde(alias = "remote_addr", skip_serializing_if = "Option::is_none")] diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index d4732be50298..dedf608603a8 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -43,7 +43,28 @@ On success, an object containing **peers** is returned. It is an array of objec - **id** (pubkey): the public key of the peer - **connected** (boolean): True if the peer is currently connected -- **channels** (array of objects): +- **log** (array of objects, optional): if *level* is specified, logs for this peer: + - **type** (string) (one of "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO\_IN", "IO\_OUT") + + If **type** is "SKIPPED": + + - **num\_skipped** (u32): number of deleted/omitted entries + + If **type** is "BROKEN", "UNUSUAL", "INFO" or "DEBUG": + + - **time** (string): UNIX timestamp with 9 decimal places + - **source** (string): The particular logbook this was found in + - **log** (string): The actual log message + - **node\_id** (pubkey): The peer this is associated with + + If **type** is "IO\_IN" or "IO\_OUT": + + - **time** (string): UNIX timestamp with 9 decimal places + - **source** (string): The particular logbook this was found in + - **log** (string): The actual log message + - **node\_id** (pubkey): The peer this is associated with + - **data** (hex): The IO which occurred +- **channels** (array of objects, optional) **deprecated, removal in v23.11**: - **state** (string): the channel state, in particular "CHANNELD\_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD\_AWAITING\_LOCKIN", "CHANNELD\_NORMAL", "CHANNELD\_SHUTTING\_DOWN", "CLOSINGD\_SIGEXCHANGE", "CLOSINGD\_COMPLETE", "AWAITING\_UNILATERAL", "FUNDING\_SPEND\_SEEN", "ONCHAIN", "DUALOPEND\_OPEN\_INIT", "DUALOPEND\_AWAITING\_LOCKIN") - **opener** (string): Who initiated the channel (one of "local", "remote") - **features** (array of strings): @@ -150,27 +171,6 @@ On success, an object containing **peers** is returned. It is an array of objec - **initial\_feerate** (string): The feerate for the initial funding transaction in per-1000-weight, with "kpw" appended - **last\_feerate** (string): The feerate for the latest funding transaction in per-1000-weight, with "kpw" appended - **next\_feerate** (string): The minimum feerate for the next funding transaction in per-1000-weight, with "kpw" appended -- **log** (array of objects, optional): if *level* is specified, logs for this peer: - - **type** (string) (one of "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO\_IN", "IO\_OUT") - - If **type** is "SKIPPED": - - - **num\_skipped** (u32): number of deleted/omitted entries - - If **type** is "BROKEN", "UNUSUAL", "INFO" or "DEBUG": - - - **time** (string): UNIX timestamp with 9 decimal places - - **source** (string): The particular logbook this was found in - - **log** (string): The actual log message - - **node\_id** (pubkey): The peer this is associated with - - If **type** is "IO\_IN" or "IO\_OUT": - - - **time** (string): UNIX timestamp with 9 decimal places - - **source** (string): The particular logbook this was found in - - **log** (string): The actual log message - - **node\_id** (pubkey): The peer this is associated with - - **data** (hex): The IO which occurred If **connected** is *true*: @@ -399,4 +399,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:c84136fcca3d0295cd1612873a54a074f3e8b6ae9cc643489cab6fb7376d66f6) +[comment]: # ( SHA256STAMP:a063a4a4fb1e6af4138d19ccbdc8d1539712c6eb6ed8a4b1b7f7c4fe12e0907b) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index b16b57f7667d..4087cfc02a8a 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -13,8 +13,7 @@ "additionalProperties": true, "required": [ "id", - "connected", - "channels" + "connected" ], "properties": { "id": { @@ -167,6 +166,7 @@ } }, "channels": { + "deprecated": "v23.02", "type": "array", "items": { "type": "object", diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 437469d6a5fd..dee6f83ca97b 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1941,16 +1941,18 @@ static void json_add_peer(struct lightningd *ld, json_add_hex_talarr(response, "features", p->their_features); } - json_array_start(response, "channels"); - json_add_uncommitted_channel(response, p->uncommitted_channel, NULL); - - list_for_each(&p->channels, channel, list) { - if (channel_unsaved(channel)) - json_add_unsaved_channel(response, channel, NULL); - else - json_add_channel(ld, response, NULL, channel, NULL); + if (deprecated_apis) { + json_array_start(response, "channels"); + json_add_uncommitted_channel(response, p->uncommitted_channel, NULL); + + list_for_each(&p->channels, channel, list) { + if (channel_unsaved(channel)) + json_add_unsaved_channel(response, channel, NULL); + else + json_add_channel(ld, response, NULL, channel, NULL); + } + json_array_end(response); } - json_array_end(response); if (ll) json_add_log(response, ld->log_book, &p->id, *ll); From 6b4166627b720b7bf5114cb723d29aae7c856746 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:55:58 +1030 Subject: [PATCH 14/14] doc: remove manual field descriptions from listpeerchannels(7). Some are best copied into the schema, but some are already out-of-date, so cleanest to remove them and rely on the generated (and thus, checked!) fields. Signed-off-by: Rusty Russell --- doc/lightning-listpeerchannels.7.md | 207 +++-------------------- doc/schemas/listpeerchannels.schema.json | 40 ++--- 2 files changed, 48 insertions(+), 199 deletions(-) diff --git a/doc/lightning-listpeerchannels.7.md b/doc/lightning-listpeerchannels.7.md index 5c4f1c95ee18..d28fb27f15f5 100644 --- a/doc/lightning-listpeerchannels.7.md +++ b/doc/lightning-listpeerchannels.7.md @@ -36,7 +36,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **perkb** (u32): Feerate per 1000 virtual bytes - **owner** (string, optional): The current subdaemon controlling this connection - **short\_channel\_id** (short\_channel\_id, optional): The short\_channel\_id (once locked in) -- **channel\_id** (hash, optional): The full channel\_id (always 64 characters) +- **channel\_id** (hash, optional): The full channel\_id (funding txid Xored with output number) (always 64 characters) - **funding\_txid** (txid, optional): ID of the funding transaction - **funding\_outnum** (u32, optional): The 0-based output number of the funding transaction which opens the channel - **initial\_feerate** (string, optional): For inflight opens, the first feerate used to initiate the channel open @@ -52,7 +52,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **scratch\_txid** (txid): The commitment transaction txid we would use if we went onchain now - **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close - **private** (boolean, optional): if False, we will not announce this channel -- **closer** (string, optional): Who initiated the channel close (one of "local", "remote") +- **closer** (string, optional): Who initiated the channel close (only present if closing) (one of "local", "remote") - **funding** (object, optional): - **local\_funds\_msat** (msat): Amount of channel we funded - **remote\_funds\_msat** (msat): Amount of channel they funded @@ -61,23 +61,23 @@ On success, an object containing **channels** is returned. It is an array of ob - **pushed\_msat** (msat, optional): Amount pushed from opener to peer - **fee\_paid\_msat** (msat, optional): Amount we paid peer at open - **fee\_rcvd\_msat** (msat, optional): Amount we were paid by peer at open -- **to\_us\_msat** (msat, optional): how much of channel is owed to us -- **min\_to\_us\_msat** (msat, optional): least amount owed to us ever -- **max\_to\_us\_msat** (msat, optional): most amount owed to us ever +- **to\_us\_msat** (msat, optional): How much of channel is owed to us +- **min\_to\_us\_msat** (msat, optional): Least amount owed to us ever. If the peer were to succesfully steal from us, this is the amount we would still retain. +- **max\_to\_us\_msat** (msat, optional): Most amount owed to us ever. If we were to successfully steal from the peer, this is the amount we could potentially get. - **total\_msat** (msat, optional): total amount in the channel - **fee\_base\_msat** (msat, optional): amount we charge to use the channel - **fee\_proportional\_millionths** (u32, optional): amount we charge to use the channel in parts-per-million -- **dust\_limit\_msat** (msat, optional): minimum amount for an output on the channel transactions -- **max\_total\_htlc\_in\_msat** (msat, optional): max amount accept in a single payment -- **their\_reserve\_msat** (msat, optional): minimum we insist they keep in channel -- **our\_reserve\_msat** (msat, optional): minimum they insist we keep in channel -- **spendable\_msat** (msat, optional): total we could send through channel -- **receivable\_msat** (msat, optional): total peer could send through channel -- **minimum\_htlc\_in\_msat** (msat, optional): the minimum amount HTLC we accept -- **minimum\_htlc\_out\_msat** (msat, optional): the minimum amount HTLC we will send -- **maximum\_htlc\_out\_msat** (msat, optional): the maximum amount HTLC we will send -- **their\_to\_self\_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close -- **our\_to\_self\_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close +- **dust\_limit\_msat** (msat, optional): Minimum amount for an output on the channel transactions +- **max\_total\_htlc\_in\_msat** (msat, optional): Max amount accept in a single payment +- **their\_reserve\_msat** (msat, optional): Minimum we insist they keep in channel (default is 1% of the total channel capacity). If they have less than this in the channel, they cannot send to us on that channel +- **our\_reserve\_msat** (msat, optional): Minimum they insist we keep in channel. If you have less than this in the channel, you cannot send out via this channel. +- **spendable\_msat** (msat, optional): An estimate of the total we could send through channel (can be wrong because adding HTLCs requires an increase in fees paid to onchain miners, and onchain fees change dynamically according to onchain activity) +- **receivable\_msat** (msat, optional): An estimate of the total peer could send through channel +- **minimum\_htlc\_in\_msat** (msat, optional): The minimum amount HTLC we accept +- **minimum\_htlc\_out\_msat** (msat, optional): The minimum amount HTLC we will send +- **maximum\_htlc\_out\_msat** (msat, optional): The maximum amount HTLC we will send +- **their\_to\_self\_delay** (u32, optional): The number of blocks before they can take their funds if they unilateral close +- **our\_to\_self\_delay** (u32, optional): The number of blocks before we can take our funds if we unilateral close - **max\_accepted\_htlcs** (u32, optional): Maximum number of incoming HTLC we will accept at once - **alias** (object, optional): - **local** (short\_channel\_id, optional): An alias assigned by this node to this channel, used for outgoing payments @@ -102,9 +102,9 @@ On success, an object containing **channels** is returned. It is an array of ob - **direction** (string): Whether it came from peer, or is going to peer (one of "in", "out") - **id** (u64): Unique ID for this htlc on this channel in this direction - **amount\_msat** (msat): Amount send/received for this HTLC - - **expiry** (u32): Block this HTLC expires at + - **expiry** (u32): Block this HTLC expires at (after which an `in` direction HTLC will be returned to the peer, an `out` returned to us). If this expiry is too close, lightningd(8) will automatically unilaterally close the channel in order to enforce the timeout onchain. - **payment\_hash** (hash): the hash of the payment\_preimage which will prove payment (always 64 characters) - - **local\_trimmed** (boolean, optional): if this is too small to enforce onchain (always *true*) + - **local\_trimmed** (boolean, optional): If this is too small to enforce onchain; it doesn't appear in the commitment transaction and will not be enforced in a unilateral close. Generally true if the HTLC (after subtracting onchain fees) is below the `dust_limit_msat` for the channel. (always *true*) - **status** (string, optional): set if this HTLC is currently waiting on a hook (and shows what plugin) If **direction** is "out": @@ -117,7 +117,7 @@ On success, an object containing **channels** is returned. It is an array of ob If **close\_to** is present: - - **close\_to\_addr** (string, optional): The bitcoin address we will close to + - **close\_to\_addr** (string, optional): The bitcoin address we will close to (present if close\_to\_addr is a standardized address) If **scratch\_txid** is present: @@ -125,7 +125,7 @@ If **scratch\_txid** is present: If **short\_channel\_id** is present: - - **direction** (u32): 0 if we're the lesser node\_id, 1 if we're the greater + - **direction** (u32): 0 if we're the lesser node\_id, 1 if we're the greater (as used in BOLT #7 channel\_update) If **inflight** is present: @@ -135,20 +135,19 @@ If **inflight** is present: [comment]: # (GENERATE-FROM-SCHEMA-END) -On success, an object with a "channels" key is returned containing a list -of 0 or more objects. If *id* and/or *status* are supplied and no matching -nodes are found, a "channels" object with an empty list is returned. +The *state* field values (and *old\_state* / *new\_state*) are worth describing further: -The objects in the *channels* array will have at least these fields: - -* *state*: Any of these strings: * `"OPENINGD"`: The channel funding protocol with the peer is ongoing and both sides are negotiating parameters. - * `"CHANNELD_AWAITING_LOCKIN"`: The peer and you have agreed on channel + * `"DUALOPEND_OPEN_INIT"`: Like `OPENINGD`, but for v2 connections which + are using collaborative opens. + * `"CHANNELD_AWAITING_LOCKIN"` / `"DUALOPEND\_AWAITING\_LOCKIN"`: The peer and you have agreed on channel parameters and are just waiting for the channel funding transaction to - be confirmed deeply. + be confirmed deeply (original and collaborative open protocols, respectively). Both you and the peer must acknowledge the channel funding transaction to be confirmed deeply before entering the next state. + Also, you can increase the onchain fee for channels in `DUALOPEND\_AWAITING\_LOCKIN` + using lightning-openchannel\_bump(7). * `"CHANNELD_NORMAL"`: The channel can be used for normal payments. * `"CHANNELD_SHUTTING_DOWN"`: A mutual close was requested (by you or peer) and both of you are waiting for HTLCs in-flight to be either @@ -168,156 +167,6 @@ The objects in the *channels* array will have at least these fields: * `"ONCHAIN"`: You saw the funding transaction getting spent and now know what happened (i.e. if it was a proper unilateral close by the peer, or a theft attempt). - * `"CLOSED"`: The channel closure has been confirmed deeply. - The channel will eventually be removed from this array. -* *state\_changes*: An array of objects describing prior state change events. -* *opener*: A string `"local"` or `"remote`" describing which side opened this - channel. -* *closer*: A string `"local"` or `"remote`" describing which side - closed this channel or `null` if the channel is not (being) closed yet. -* *status*: An array of strings containing the most important log messages - relevant to this channel. - Also known as the "billboard". -* *owner*: A string describing which particular sub-daemon of `lightningd` - currently is responsible for this channel. - One of: `"lightning_openingd"`, `"lightning_channeld"`, - `"lightning_closingd"`, `"lightning_onchaind"`. -* *to\_us\_msat*: A string describing how much of the funds is owned by us; - a number followed by a string unit. -* *total\_msat*: A string describing the total capacity of the channel; - a number followed by a string unit. -* *fee\_base\_msat*: The fixed routing fee we charge for forwards going out over - this channel, regardless of payment size. -* *fee\_proportional\_millionths*: The proportional routing fees in ppm (parts- - per-millionths) we charge for forwards going out over this channel. -* *features*: An array of feature names supported by this channel. - -These fields may exist if the channel has gotten beyond the `"OPENINGD"` -state, or in various circumstances: - -* *short\_channel\_id*: A string of the short channel ID for the channel; - Format is `"BBBBxTTTxOOO"`, where `"BBBB"` is the numeric block height - at which the funding transaction was confirmed, `"TTT"` is the numeric - funding transaction index within that block, and `"OOO"` is the - numeric output index of the transaction output that actually anchors - this channel. -* *direction*: The channel-direction we own, as per BOLT \#7. - We own channel-direction 0 if our node ID is "less than" the peer node ID - in a lexicographical ordering of our node IDs, otherwise we own - channel-direction 1. - Our `channel_update` will use this *direction*. -* *channel\_id*: The full channel ID of the channel; - the funding transaction ID XORed with the output number. -* *funding\_txid*: The funding transaction ID of the channel. -* *close\_to*: The raw `scriptPubKey` that was indicated in the starting - **fundchannel\_start** command and accepted by the peer. - If the `scriptPubKey` encodes a standardized address, an additional - *close\_to\_addr* field will be present with the standardized address. -* *private*: A boolean, true if the channel is unpublished, false if the - channel is published. -* *funding\_msat*: An object, whose field names are the node - IDs involved in the channel, and whose values are strings (numbers with - a unit suffix) indicating how much that node originally contributed in - opening the channel. -* *min\_to\_us\_msat*: A string describing the historic point at which - we owned the least amount of funds in this channel; - a number followed by a string unit. - If the peer were to succesfully steal from us, this is the amount we - would still retain. -* *max\_to\_us\_msat*: A string describing the historic point at which - we owned the most amount of funds in this channel; - a number followed by a string unit. - If we were to successfully steal from the peer, this is the amount we - could potentially get. -* *dust\_limit\_msat*: A string describing an amount; - if an HTLC or the amount wholly-owned by one node is at or below this - amount, it will be considered "dusty" and will not appear in a close - transaction, and will be donated to miners as fee; - a number followed by a string unit. -* *max\_total\_htlc\_in\_msat*: A string describing an amount; - the sum of all HTLCs in the channel cannot exceed this amount; - a number followed by a string unit. -* *their\_reserve\_msat*: A string describing the minimum amount that - the peer must keep in the channel when it attempts to send out; - if it has less than this in the channel, it cannot send to us on - that channel; - a number followed by a string unit. - We impose this on them, default is 1% of the total channel capacity. -* *our\_reserve\_msat*: A string describing the minimum amount that - you must keep in the channel when you attempt to send out; - if you have less than this in the channel, you cannot send out - via this channel; - a number followed by a string unit. - The peer imposes this on us, default is 1% of the total channel capacity. -* *spendable\_msat* and *receivable\_msat*: A string describing an - ***estimate*** of how much we can send or receive over this channel in a - single payment (or payment-part for multi-part payments); - a number followed by a string unit. - This is an ***estimate***, which can be wrong because adding HTLCs requires - an increase in fees paid to onchain miners, and onchain fees change - dynamically according to onchain activity. - For a sufficiently-large channel, this can be limited by the rules imposed - under certain blockchains; - for example, individual Bitcoin mainnet payment-parts cannot exceed - 42.94967295 mBTC. -* *minimum\_htlc\_in\_msat*: A string describing the minimum amount that - an HTLC must have before we accept it. -* *their\_to\_self\_delay*: The number of blocks that the peer must wait - to claim their funds, if they close unilaterally. -* *our\_to\_self\_delay*: The number of blocks that you must wait to claim - your funds, if you close unilaterally. -* *max\_accepted\_htlcs*: The maximum number of HTLCs you will accept on - this channel. -* *in\_payments\_offered*: The number of incoming HTLCs offered over this - channel. -* *in\_offered\_msat*: A string describing the total amount of all incoming - HTLCs offered over this channel; - a number followed by a string unit. -* *in\_payments\_fulfilled*: The number of incoming HTLCs offered *and - successfully claimed* over this channel. -* *in\_fulfilled\_msat*: A string describing the total amount of all - incoming HTLCs offered *and successfully claimed* over this channel; - a number followed by a string unit. -* *out\_payments\_offered*: The number of outgoing HTLCs offered over - this channel. -* *out\_offered\_msat*: A string describing the total amount of all - outgoing HTLCs offered over this channel; - a number followed by a string unit. -* *out\_payments\_fulfilled*: The number of outgoing HTLCs offered *and - successfully claimed* over this channel. -* *out\_fulfilled\_msat*: A string describing the total amount of all - outgoing HTLCs offered *and successfully claimed* over this channel; - a number followed by a string unit. -* *scratch\_txid*: The txid of the latest transaction (what we would sign and - send to chain if the channel were to fail now). -* *last\_tx\_fee*: The fee on that latest transaction. -* *feerate*: An object containing the latest feerate as both *perkw* and *perkb*. -* *htlcs*: An array of objects describing the HTLCs currently in-flight - in the channel. - -Objects in the *htlcs* array will contain these fields: - -* *direction*: Either the string `"out"` or `"in"`, whether it is an - outgoing or incoming HTLC. -* *id*: A numeric ID uniquely identifying this HTLC. -* *amount\_msat*: The value of the HTLC. -* *expiry*: The blockheight at which the HTLC will be forced to return - to its offerer: an `"in"` HTLC will be returned to the peer, an - `"out"` HTLC will be returned to you. - **NOTE** If the *expiry* of any outgoing HTLC will arrive in the next - block, `lightningd`(8) will automatically unilaterally close the - channel in order to enforce the timeout onchain. -* *payment\_hash*: The payment hash, whose preimage must be revealed to - successfully claim this HTLC. -* *state*: A string describing whether the HTLC has been communicated to - or from the peer, whether it has been signed in a new commitment, whether - the previous commitment (that does not contain it) has been revoked, as - well as when the HTLC is fulfilled or failed offchain. -* *local\_trimmed*: A boolean, existing and `true` if the HTLC is not - actually instantiated as an output (i.e. "trimmed") on the commitment - transaction (and will not be instantiated on a unilateral close). - Generally true if the HTLC is below the *dust\_limit\_msat* for the - channel. On error the returned object will contain `code` and `message` properties, with `code` being one of the following: @@ -342,4 +191,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:a9e27c78498192757d4972da091715cadd8dbf2f0c08c288e6c600626567f379) +[comment]: # ( SHA256STAMP:485344d9d9a47bf3093c8e54fcf80bea14623e2611f2a4e2d2b5c723d8e6094b) diff --git a/doc/schemas/listpeerchannels.schema.json b/doc/schemas/listpeerchannels.schema.json index 3f5d21d98a5e..97e58c18b0e9 100644 --- a/doc/schemas/listpeerchannels.schema.json +++ b/doc/schemas/listpeerchannels.schema.json @@ -77,7 +77,7 @@ }, "channel_id": { "type": "hash", - "description": "The full channel_id", + "description": "The full channel_id (funding txid Xored with output number)", "minLength": 64, "maxLength": 64 }, @@ -169,7 +169,7 @@ "local", "remote" ], - "description": "Who initiated the channel close" + "description": "Who initiated the channel close (only present if closing)" }, "features": { "type": "array", @@ -223,15 +223,15 @@ }, "to_us_msat": { "type": "msat", - "description": "how much of channel is owed to us" + "description": "How much of channel is owed to us" }, "min_to_us_msat": { "type": "msat", - "description": "least amount owed to us ever" + "description": "Least amount owed to us ever. If the peer were to succesfully steal from us, this is the amount we would still retain." }, "max_to_us_msat": { "type": "msat", - "description": "most amount owed to us ever" + "description": "Most amount owed to us ever. If we were to successfully steal from the peer, this is the amount we could potentially get." }, "total_msat": { "type": "msat", @@ -247,47 +247,47 @@ }, "dust_limit_msat": { "type": "msat", - "description": "minimum amount for an output on the channel transactions" + "description": "Minimum amount for an output on the channel transactions" }, "max_total_htlc_in_msat": { "type": "msat", - "description": "max amount accept in a single payment" + "description": "Max amount accept in a single payment" }, "their_reserve_msat": { "type": "msat", - "description": "minimum we insist they keep in channel" + "description": "Minimum we insist they keep in channel (default is 1% of the total channel capacity). If they have less than this in the channel, they cannot send to us on that channel" }, "our_reserve_msat": { "type": "msat", - "description": "minimum they insist we keep in channel" + "description": "Minimum they insist we keep in channel. If you have less than this in the channel, you cannot send out via this channel." }, "spendable_msat": { "type": "msat", - "description": "total we could send through channel" + "description": "An estimate of the total we could send through channel (can be wrong because adding HTLCs requires an increase in fees paid to onchain miners, and onchain fees change dynamically according to onchain activity)" }, "receivable_msat": { "type": "msat", - "description": "total peer could send through channel" + "description": "An estimate of the total peer could send through channel" }, "minimum_htlc_in_msat": { "type": "msat", - "description": "the minimum amount HTLC we accept" + "description": "The minimum amount HTLC we accept" }, "minimum_htlc_out_msat": { "type": "msat", - "description": "the minimum amount HTLC we will send" + "description": "The minimum amount HTLC we will send" }, "maximum_htlc_out_msat": { "type": "msat", - "description": "the maximum amount HTLC we will send" + "description": "The maximum amount HTLC we will send" }, "their_to_self_delay": { "type": "u32", - "description": "the number of blocks before they can take their funds if they unilateral close" + "description": "The number of blocks before they can take their funds if they unilateral close" }, "our_to_self_delay": { "type": "u32", - "description": "the number of blocks before we can take our funds if we unilateral close" + "description": "The number of blocks before we can take our funds if we unilateral close" }, "max_accepted_htlcs": { "type": "u32", @@ -498,7 +498,7 @@ }, "expiry": { "type": "u32", - "description": "Block this HTLC expires at" + "description": "Block this HTLC expires at (after which an `in` direction HTLC will be returned to the peer, an `out` returned to us). If this expiry is too close, lightningd(8) will automatically unilaterally close the channel in order to enforce the timeout onchain." }, "payment_hash": { "type": "hash", @@ -511,7 +511,7 @@ "enum": [ true ], - "description": "if this is too small to enforce onchain" + "description": "If this is too small to enforce onchain; it doesn't appear in the commitment transaction and will not be enforced in a unilateral close. Generally true if the HTLC (after subtracting onchain fees) is below the `dust_limit_msat` for the channel." }, "status": { "type": "string", @@ -696,7 +696,7 @@ "direction": {}, "close_to_addr": { "type": "string", - "description": "The bitcoin address we will close to" + "description": "The bitcoin address we will close to (present if close_to_addr is a standardized address)" } } } @@ -872,7 +872,7 @@ "last_tx_fee_msat": {}, "direction": { "type": "u32", - "description": "0 if we're the lesser node_id, 1 if we're the greater" + "description": "0 if we're the lesser node_id, 1 if we're the greater (as used in BOLT #7 channel_update)" } } }