mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
peer_control: Make close wait for complete closure, with timeout.
Also report tx and txid, and whether we closed unilaterally or bilaterally, if we could close the channel. Also make a manpage. Fixes: #1207 Fixes: #714 Fixes: #622
This commit is contained in:
parent
4673ba6a0a
commit
2cee1ab20f
@ -333,12 +333,16 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
}
|
||||
return self.call("fundchannel", payload)
|
||||
|
||||
def close(self, peer_id):
|
||||
def close(self, peer_id, force=None, timeout=None):
|
||||
"""
|
||||
Close the channel with peer {id}
|
||||
Close the channel with peer {id}, forcing a unilateral
|
||||
close if {force} is True, and timing out with {timeout}
|
||||
seconds.
|
||||
"""
|
||||
payload = {
|
||||
"id": peer_id
|
||||
"id": peer_id,
|
||||
"force": force,
|
||||
"timeout": timeout
|
||||
}
|
||||
return self.call("close", payload)
|
||||
|
||||
|
@ -6,6 +6,7 @@ doc-wrongdir:
|
||||
|
||||
MANPAGES := doc/lightning-cli.1 \
|
||||
doc/lightning-autocleaninvoice.7 \
|
||||
doc/lightning-close.7 \
|
||||
doc/lightning-decodepay.7 \
|
||||
doc/lightning-delexpiredinvoice.7 \
|
||||
doc/lightning-delinvoice.7 \
|
||||
|
59
doc/lightning-close.7
Normal file
59
doc/lightning-close.7
Normal file
@ -0,0 +1,59 @@
|
||||
'\" t
|
||||
.\" Title: lightning-close
|
||||
.\" Author: [see the "AUTHOR" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||
.\" Date: 04/15/2018
|
||||
.\" Manual: \ \&
|
||||
.\" Source: \ \&
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "LIGHTNING\-CLOSE" "7" "04/15/2018" "\ \&" "\ \&"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.\" http://bugs.debian.org/507673
|
||||
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * set default formatting
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" disable hyphenation
|
||||
.nh
|
||||
.\" disable justification (adjust text to left margin only)
|
||||
.ad l
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * MAIN CONTENT STARTS HERE *
|
||||
.\" -----------------------------------------------------------------
|
||||
.SH "NAME"
|
||||
lightning-close \- Protocol for closing channels with direct peers
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBclose\fR \fIid\fR [\fIforce\fR] [\fItimeout\fR]
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
The \fBclose\fR RPC command attempts to close the channel cooperatively with the peer\&. It applies to the active channel of the direct peer corresponding to the given peer \fIid\fR\&.
|
||||
.sp
|
||||
The \fBclose\fR command will time out and return with an error when the number of seconds specified in \fItimeout\fR is reached\&. If unspecified, it times out in 30 seconds\&.
|
||||
.sp
|
||||
The \fIforce\fR argument, if the JSON value \fItrue\fR, will cause the channel to be unilaterally closed when the timeout is reached\&. If so, timeout will not cause an error, but instead cause the channel to be failed and put onchain unilaterally\&. Unilateral closes will lead to your funds getting locked according to the \fIto_self_delay\fR parameter of the peer\&.
|
||||
.sp
|
||||
Normally the peer needs to be live and connected in order to negotiate a mutual close\&. Forcing a unilateral close can be used if you suspect you can no longer contact the peer\&.
|
||||
.SH "RETURN VALUE"
|
||||
.sp
|
||||
On success, an object with fields \fItx\fR and \fItxid\fR containing the closing transaction are returned\&. It will also have a field \fItype\fR which is either the JSON string \fImutual\fR or the JSON string \fIunilateral\fR\&. A \fImutual\fR close means that we could negotiate a close with the peer, while a \fIunilateral\fR close means that the \fIforce\fR flag was set and we had to close the channel without waiting for the counterparty\&.
|
||||
.sp
|
||||
A unilateral close may still occur with \fIforce\fR set to \fIfalse\fR if the peer did not behave correctly during the close negotiation\&.
|
||||
.sp
|
||||
Unilateral closes will return your funds after a delay\&. The delay will vary based on the peer \fIto_self_delay\fR setting, not your own setting\&.
|
||||
.sp
|
||||
On failure, if \fBclose\fR failed due to timing out with \fIforce\fR argument \fIfalse\fR, the channel will still eventually close once we have contacted the peer\&.
|
||||
.SH "AUTHOR"
|
||||
.sp
|
||||
ZmnSCPxj <ZmnSCPxj@protonmail\&.com> is mainly responsible\&.
|
||||
.SH "SEE ALSO"
|
||||
.SH "RESOURCES"
|
||||
.sp
|
||||
Main web site: https://github\&.com/ElementsProject/lightning
|
70
doc/lightning-close.7.txt
Normal file
70
doc/lightning-close.7.txt
Normal file
@ -0,0 +1,70 @@
|
||||
LIGHTNING-CLOSE(7)
|
||||
==================
|
||||
:doctype: manpage
|
||||
|
||||
NAME
|
||||
----
|
||||
lightning-close - Protocol for closing channels with direct peers
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*close* 'id' ['force'] ['timeout']
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
The *close* RPC command attempts to close the channel cooperatively
|
||||
with the peer.
|
||||
It applies to the active channel of the direct peer corresponding to
|
||||
the given peer 'id'.
|
||||
|
||||
The *close* command will time out and return with an error when the
|
||||
number of seconds specified in 'timeout' is reached.
|
||||
If unspecified, it times out in 30 seconds.
|
||||
|
||||
The 'force' argument, if the JSON value 'true', will cause the
|
||||
channel to be unilaterally closed when the timeout is reached.
|
||||
If so, timeout will not cause an error, but instead cause the
|
||||
channel to be failed and put onchain unilaterally.
|
||||
Unilateral closes will lead to your funds getting locked according
|
||||
to the 'to_self_delay' parameter of the peer.
|
||||
|
||||
Normally the peer needs to be live and connected in order to negotiate
|
||||
a mutual close.
|
||||
Forcing a unilateral close can be used if you suspect you can no longer
|
||||
contact the peer.
|
||||
|
||||
RETURN VALUE
|
||||
------------
|
||||
|
||||
On success, an object with fields 'tx' and 'txid' containing the
|
||||
closing transaction are returned.
|
||||
It will also have a field 'type' which is either the JSON string
|
||||
'mutual' or the JSON string 'unilateral'.
|
||||
A 'mutual' close means that we could negotiate a close with the
|
||||
peer, while a 'unilateral' close means that the 'force' flag was
|
||||
set and we had to close the channel without waiting for the
|
||||
counterparty.
|
||||
|
||||
A unilateral close may still occur with 'force' set to 'false' if
|
||||
the peer did not behave correctly during the close negotiation.
|
||||
|
||||
Unilateral closes will return your funds after a delay.
|
||||
The delay will vary based on the peer 'to_self_delay' setting, not
|
||||
your own setting.
|
||||
|
||||
On failure, if *close* failed due to timing out with 'force'
|
||||
argument 'false', the channel will still eventually close once
|
||||
we have contacted the peer.
|
||||
|
||||
AUTHOR
|
||||
------
|
||||
ZmnSCPxj <ZmnSCPxj@protonmail.com> is mainly responsible.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
RESOURCES
|
||||
---------
|
||||
Main web site: https://github.com/ElementsProject/lightning
|
@ -321,7 +321,8 @@ void channel_fail_permanent(struct channel *channel, const char *fmt, ...)
|
||||
}
|
||||
|
||||
channel_set_owner(channel, NULL);
|
||||
drop_to_chain(ld, channel);
|
||||
/* Drop non-cooperatively (unilateral) to chain. */
|
||||
drop_to_chain(ld, channel, false);
|
||||
tal_free(why);
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,8 @@ static void peer_closing_complete(struct channel *channel, const u8 *msg)
|
||||
if (channel->state == CLOSINGD_COMPLETE)
|
||||
return;
|
||||
|
||||
drop_to_chain(channel->peer->ld, channel);
|
||||
/* Channel gets dropped to chain cooperatively. */
|
||||
drop_to_chain(channel->peer->ld, channel, true);
|
||||
channel_set_state(channel, CLOSINGD_SIGEXCHANGE, CLOSINGD_COMPLETE);
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
|
||||
list_head_init(&ld->connects);
|
||||
list_head_init(&ld->waitsendpay_commands);
|
||||
list_head_init(&ld->sendpay_commands);
|
||||
list_head_init(&ld->close_commands);
|
||||
ld->wireaddrs = tal_arr(ld, struct wireaddr, 0);
|
||||
ld->portnum = DEFAULT_PORT;
|
||||
timers_init(&ld->timers, time_mono());
|
||||
|
@ -138,6 +138,8 @@ struct lightningd {
|
||||
struct list_head waitsendpay_commands;
|
||||
/* Outstanding sendpay commands. */
|
||||
struct list_head sendpay_commands;
|
||||
/* Outstanding close commands. */
|
||||
struct list_head close_commands;
|
||||
|
||||
/* Maintained by invoices.c */
|
||||
struct invoices *invoices;
|
||||
|
@ -44,6 +44,17 @@
|
||||
#include <wally_bip32.h>
|
||||
#include <wire/gen_onion_wire.h>
|
||||
|
||||
struct close_command {
|
||||
/* Inside struct lightningd close_commands. */
|
||||
struct list_node list;
|
||||
/* Command structure. This is the parent of the close command. */
|
||||
struct command *cmd;
|
||||
/* Channel being closed. */
|
||||
struct channel *channel;
|
||||
/* Should we force the close on timeout? */
|
||||
bool force;
|
||||
};
|
||||
|
||||
static void destroy_peer(struct peer *peer)
|
||||
{
|
||||
list_del_from(&peer->ld->peers, &peer->list);
|
||||
@ -209,13 +220,126 @@ static void remove_sig(struct bitcoin_tx *signed_tx)
|
||||
signed_tx->input[0].witness = tal_free(signed_tx->input[0].witness);
|
||||
}
|
||||
|
||||
void drop_to_chain(struct lightningd *ld, struct channel *channel)
|
||||
/* Resolve a single close command. */
|
||||
static void
|
||||
resolve_one_close_command(struct close_command *cc, bool cooperative)
|
||||
{
|
||||
struct json_result *result = new_json_result(cc);
|
||||
u8 *tx = linearize_tx(result, cc->channel->last_tx);
|
||||
struct bitcoin_txid txid;
|
||||
|
||||
bitcoin_txid(cc->channel->last_tx, &txid);
|
||||
|
||||
json_object_start(result, NULL);
|
||||
json_add_hex(result, "tx", tx, tal_len(tx));
|
||||
json_add_txid(result, "txid", &txid);
|
||||
if (cooperative)
|
||||
json_add_string(result, "type", "mutual");
|
||||
else
|
||||
json_add_string(result, "type", "unilateral");
|
||||
json_object_end(result);
|
||||
|
||||
command_success(cc->cmd, result);
|
||||
}
|
||||
|
||||
/* Resolve a close command for a channel that will be closed soon. */
|
||||
static void
|
||||
resolve_close_command(struct lightningd *ld, struct channel *channel,
|
||||
bool cooperative)
|
||||
{
|
||||
struct close_command *cc;
|
||||
struct close_command *n;
|
||||
|
||||
list_for_each_safe (&ld->close_commands, cc, n, list) {
|
||||
if (cc->channel != channel)
|
||||
continue;
|
||||
resolve_one_close_command(cc, cooperative);
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy the close command structure in reaction to the
|
||||
* channel being destroyed. */
|
||||
static void
|
||||
destroy_close_command_on_channel_destroy(struct channel *_ UNUSED,
|
||||
struct close_command *cc)
|
||||
{
|
||||
/* The cc has the command as parent, so resolving the
|
||||
* command destroys the cc and triggers destroy_close_command.
|
||||
* Clear the cc->channel first so that we will not try to
|
||||
* remove a destructor. */
|
||||
cc->channel = NULL;
|
||||
command_fail(cc->cmd, "Channel forgotten before proper close.");
|
||||
}
|
||||
|
||||
/* Destroy the close command structure. */
|
||||
static void
|
||||
destroy_close_command(struct close_command *cc)
|
||||
{
|
||||
list_del(&cc->list);
|
||||
/* If destroy_close_command_on_channel_destroy was
|
||||
* triggered beforehand, it will have cleared
|
||||
* the channel field, preventing us from removing it
|
||||
* from an already-destroyed channel. */
|
||||
if (!cc->channel)
|
||||
return;
|
||||
tal_del_destructor2(cc->channel,
|
||||
&destroy_close_command_on_channel_destroy,
|
||||
cc);
|
||||
}
|
||||
|
||||
/* Handle timeout. */
|
||||
static void
|
||||
close_command_timeout(struct close_command *cc)
|
||||
{
|
||||
if (cc->force)
|
||||
/* This will trigger drop_to_chain, which will trigger
|
||||
* resolution of the command and destruction of the
|
||||
* close_command. */
|
||||
channel_fail_permanent(cc->channel,
|
||||
"Forcibly closed by 'close' command timeout");
|
||||
else
|
||||
/* Fail the command directly, which will resolve the
|
||||
* command and destroy the close_command. */
|
||||
command_fail(cc->cmd,
|
||||
"Channel close negotiation not finished "
|
||||
"before timeout");
|
||||
}
|
||||
|
||||
/* Construct a close command structure and add to ld. */
|
||||
static void
|
||||
register_close_command(struct lightningd *ld,
|
||||
struct command *cmd,
|
||||
struct channel *channel,
|
||||
unsigned int timeout,
|
||||
bool force)
|
||||
{
|
||||
struct close_command *cc;
|
||||
assert(channel);
|
||||
|
||||
cc = tal(cmd, struct close_command);
|
||||
list_add_tail(&ld->close_commands, &cc->list);
|
||||
cc->cmd = cmd;
|
||||
cc->channel = channel;
|
||||
cc->force = force;
|
||||
tal_add_destructor(cc, &destroy_close_command);
|
||||
tal_add_destructor2(channel,
|
||||
&destroy_close_command_on_channel_destroy,
|
||||
cc);
|
||||
new_reltimer(&ld->timers, cc, time_from_sec(timeout),
|
||||
&close_command_timeout, cc);
|
||||
}
|
||||
|
||||
void drop_to_chain(struct lightningd *ld, struct channel *channel,
|
||||
bool cooperative)
|
||||
{
|
||||
sign_last_tx(channel);
|
||||
|
||||
/* Keep broadcasting until we say stop (can fail due to dup,
|
||||
* if they beat us to the broadcast). */
|
||||
broadcast_tx(ld->topology, channel, channel->last_tx, NULL);
|
||||
|
||||
resolve_close_command(ld, channel, cooperative);
|
||||
|
||||
remove_sig(channel->last_tx);
|
||||
}
|
||||
|
||||
@ -854,11 +978,17 @@ static void json_close(struct command *cmd,
|
||||
const char *buffer, const jsmntok_t *params)
|
||||
{
|
||||
jsmntok_t *peertok;
|
||||
jsmntok_t *timeouttok;
|
||||
jsmntok_t *forcetok;
|
||||
struct peer *peer;
|
||||
struct channel *channel;
|
||||
unsigned int timeout = 30;
|
||||
bool force = false;
|
||||
|
||||
if (!json_get_params(cmd, buffer, params,
|
||||
"id", &peertok,
|
||||
"?force", &forcetok,
|
||||
"?timeout", &timeouttok,
|
||||
NULL)) {
|
||||
return;
|
||||
}
|
||||
@ -868,6 +998,18 @@ static void json_close(struct command *cmd,
|
||||
command_fail(cmd, "Could not find peer with that id");
|
||||
return;
|
||||
}
|
||||
if (forcetok && !json_tok_bool(buffer, forcetok, &force)) {
|
||||
command_fail(cmd, "Force '%.*s' must be true or false",
|
||||
forcetok->end - forcetok->start,
|
||||
buffer + forcetok->start);
|
||||
return;
|
||||
}
|
||||
if (timeouttok && !json_tok_number(buffer, timeouttok, &timeout)) {
|
||||
command_fail(cmd, "Timeout '%.*s' is not a number",
|
||||
timeouttok->end - timeouttok->start,
|
||||
buffer + timeouttok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
channel = peer_active_channel(peer);
|
||||
if (!channel) {
|
||||
@ -883,7 +1025,22 @@ static void json_close(struct command *cmd,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Normal case. */
|
||||
/* Normal case.
|
||||
* We allow states shutting down and sigexchange; a previous
|
||||
* close command may have timed out, and this current command
|
||||
* will continue waiting for the effects of the previous
|
||||
* close command. */
|
||||
if (channel->state != CHANNELD_NORMAL &&
|
||||
channel->state != CHANNELD_AWAITING_LOCKIN &&
|
||||
channel->state != CHANNELD_SHUTTING_DOWN &&
|
||||
channel->state != CLOSINGD_SIGEXCHANGE)
|
||||
command_fail(cmd, "Peer is in state %s",
|
||||
channel_state_name(channel));
|
||||
|
||||
/* If normal or locking in, transition to shutting down
|
||||
* state.
|
||||
* (if already shutting down or sigexchange, just keep
|
||||
* waiting) */
|
||||
if (channel->state == CHANNELD_NORMAL || channel->state == CHANNELD_AWAITING_LOCKIN) {
|
||||
channel_set_state(channel,
|
||||
channel->state, CHANNELD_SHUTTING_DOWN);
|
||||
@ -891,11 +1048,20 @@ static void json_close(struct command *cmd,
|
||||
if (channel->owner)
|
||||
subd_send_msg(channel->owner,
|
||||
take(towire_channel_send_shutdown(channel)));
|
||||
}
|
||||
/* If channel has no owner, it means the peer is disconnected,
|
||||
* so make a nominal effort to contact it now.
|
||||
*/
|
||||
if (!channel->owner)
|
||||
subd_send_msg(cmd->ld->gossip,
|
||||
take(towire_gossipctl_reach_peer(NULL,
|
||||
&channel->peer->id)));
|
||||
|
||||
command_success(cmd, null_response(cmd));
|
||||
} else
|
||||
command_fail(cmd, "Peer is in state %s",
|
||||
channel_state_name(channel));
|
||||
/* Register this command for later handling. */
|
||||
register_close_command(cmd->ld, cmd, channel, timeout, force);
|
||||
|
||||
/* Wait until close drops down to chain. */
|
||||
command_still_pending(cmd);
|
||||
}
|
||||
|
||||
static const struct json_command close_command = {
|
||||
|
@ -102,7 +102,7 @@ u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx);
|
||||
/* We've loaded peers from database, set them going. */
|
||||
void activate_peers(struct lightningd *ld);
|
||||
|
||||
void drop_to_chain(struct lightningd *ld, struct channel *channel);
|
||||
void drop_to_chain(struct lightningd *ld, struct channel *channel, bool cooperative);
|
||||
|
||||
/* Get range of feerates to insist other side abide by for normal channels. */
|
||||
u32 feerate_min(struct lightningd *ld);
|
||||
|
@ -1180,8 +1180,10 @@ class LightningDTests(BaseLightningDTests):
|
||||
billboard = l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'][0]['status']
|
||||
assert billboard == ['CHANNELD_NORMAL:Funding transaction locked. Channel announced.']
|
||||
|
||||
# This should return, then close.
|
||||
l1.rpc.close(l2.info['id'])
|
||||
# This should return with an error, then close.
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN')
|
||||
l2.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN')
|
||||
|
||||
@ -1306,7 +1308,9 @@ class LightningDTests(BaseLightningDTests):
|
||||
|
||||
# Now close
|
||||
for p in peers:
|
||||
l1.rpc.close(p.info['id'])
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, p.info['id'], False, 0)
|
||||
|
||||
for p in peers:
|
||||
p.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
@ -3118,8 +3122,10 @@ class LightningDTests(BaseLightningDTests):
|
||||
|
||||
assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 0
|
||||
|
||||
# This should return, then close.
|
||||
l1.rpc.close(l2.info['id'])
|
||||
# This should return with an error, then close.
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN')
|
||||
l2.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN')
|
||||
|
||||
@ -3144,7 +3150,10 @@ class LightningDTests(BaseLightningDTests):
|
||||
l1.daemon.wait_for_log('sendrawtx exit 0')
|
||||
bitcoind.generate_block(1)
|
||||
|
||||
l1.rpc.close(l2.info['id'])
|
||||
# This should return with an error, then close.
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log('CHANNELD_AWAITING_LOCKIN to CHANNELD_SHUTTING_DOWN')
|
||||
l2.daemon.wait_for_log('CHANNELD_AWAITING_LOCKIN to CHANNELD_SHUTTING_DOWN')
|
||||
|
||||
@ -3179,8 +3188,10 @@ class LightningDTests(BaseLightningDTests):
|
||||
|
||||
assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 0
|
||||
|
||||
# This should return, then close.
|
||||
l1.rpc.close(l2.info['id'])
|
||||
# This should return with an error, then close.
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN')
|
||||
l2.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN')
|
||||
|
||||
@ -3852,7 +3863,9 @@ class LightningDTests(BaseLightningDTests):
|
||||
self.pay(l2, l1, 100000000)
|
||||
|
||||
# Now shutdown cleanly.
|
||||
l1.rpc.close(l2.info['id'])
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
|
||||
@ -3896,8 +3909,10 @@ class LightningDTests(BaseLightningDTests):
|
||||
l1.rpc.dev_setfees()
|
||||
l1.daemon.wait_for_log('dev-setfees: fees now 21098/7654/321')
|
||||
|
||||
# Now shutdown cleanly.
|
||||
l1.rpc.close(l2.info['id'])
|
||||
# This should return with an error, then close.
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
|
||||
@ -3936,8 +3951,10 @@ class LightningDTests(BaseLightningDTests):
|
||||
# 15sat/byte fee
|
||||
l1.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK')
|
||||
|
||||
# Now shutdown cleanly.
|
||||
l1.rpc.close(l3.info['id'])
|
||||
# This should return with an error, then close.
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l3.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
l3.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
|
||||
@ -3968,7 +3985,9 @@ class LightningDTests(BaseLightningDTests):
|
||||
assert l2.daemon.is_in_log('got commitsig [0-9]*: feerate 14000')
|
||||
|
||||
# Now shutdown cleanly.
|
||||
l1.rpc.close(l2.info['id'])
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
|
||||
@ -4104,7 +4123,9 @@ class LightningDTests(BaseLightningDTests):
|
||||
l2.daemon.wait_for_log('Handing back peer .* to master')
|
||||
self.fund_channel(l1, l2, 10**6)
|
||||
|
||||
l1.rpc.close(l2.info['id'])
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
|
||||
|
||||
@ -4249,7 +4270,9 @@ class LightningDTests(BaseLightningDTests):
|
||||
assert l1.rpc.getpeer(l2.info['id'])['color'] == l1.rpc.listnodes(l2.info['id'])['nodes'][0]['color']
|
||||
|
||||
# Close the channel to forget the peer
|
||||
l1.rpc.close(l2.info['id'])
|
||||
self.assertRaisesRegex(ValueError,
|
||||
"Channel close negotiation not finished",
|
||||
l1.rpc.close, l2.info['id'], False, 0)
|
||||
l1.daemon.wait_for_log('Forgetting remote peer')
|
||||
bitcoind.generate_block(100)
|
||||
l1.daemon.wait_for_log('WIRE_ONCHAIN_ALL_IRREVOCABLY_RESOLVED')
|
||||
|
@ -253,6 +253,10 @@ bool json_tok_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, b
|
||||
bool json_tok_loglevel(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
|
||||
enum log_level *level UNNEEDED)
|
||||
{ fprintf(stderr, "json_tok_loglevel called!\n"); abort(); }
|
||||
/* Generated stub for json_tok_number */
|
||||
bool json_tok_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
|
||||
unsigned int *num UNNEEDED)
|
||||
{ fprintf(stderr, "json_tok_number called!\n"); abort(); }
|
||||
/* Generated stub for json_tok_pubkey */
|
||||
bool json_tok_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
|
||||
struct pubkey *pubkey UNNEEDED)
|
||||
|
Loading…
Reference in New Issue
Block a user