diff --git a/doc/lightning-close.7 b/doc/lightning-close.7
index a73594afe0a0..ce0d489b29b5 100644
--- a/doc/lightning-close.7
+++ b/doc/lightning-close.7
@@ -2,12 +2,12 @@
.\" Title: lightning-close
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1
-.\" Date: 04/15/2018
+.\" Date: 04/30/2018
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
-.TH "LIGHTNING\-CLOSE" "7" "04/15/2018" "\ \&" "\ \&"
+.TH "LIGHTNING\-CLOSE" "7" "04/30/2018" "\ \&" "\ \&"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -34,7 +34,7 @@ lightning-close \- Command for closing channels with direct peers
\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\&.
+The \fBclose\fR RPC command attempts to close the channel cooperatively with the peer\&. If the given \fIid\fR is a peer ID (66 hex digits as a string), then it applies to the active channel of the direct peer corresponding to the given peer ID\&. If the given \fIid\fR is a channel ID (64 hex digits as a string, or the short channel ID \fIblockheight:txindex:outindex\fR form), then it applies to that channel\&.
.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
diff --git a/doc/lightning-close.7.txt b/doc/lightning-close.7.txt
index 7c6dd4d145be..cb84447c8b9a 100644
--- a/doc/lightning-close.7.txt
+++ b/doc/lightning-close.7.txt
@@ -15,8 +15,12 @@ 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'.
+If the given 'id' is a peer ID (66 hex digits as a string), then
+it applies to the active channel of the direct peer corresponding to
+the given peer ID.
+If the given 'id' is a channel ID (64 hex digits as a string, or
+the short channel ID 'blockheight:txindex:outindex' form), then it
+applies to that channel.
The *close* command will time out and return with an error when the
number of seconds specified in 'timeout' is reached.
diff --git a/lightningd/json.c b/lightningd/json.c
index 540507e23e50..05ab73bb3fc3 100644
--- a/lightningd/json.c
+++ b/lightningd/json.c
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
/* Output a route hop */
static void
@@ -110,6 +111,14 @@ bool json_tok_short_channel_id(const char *buffer, const jsmntok_t *tok,
scid);
}
+bool
+json_tok_channel_id(const char *buffer, const jsmntok_t *tok,
+ struct channel_id *cid)
+{
+ return hex_decode(buffer + tok->start, tok->end - tok->start,
+ cid, sizeof(*cid));
+}
+
void json_add_address(struct json_result *response, const char *fieldname,
const struct wireaddr *addr)
{
diff --git a/lightningd/json.h b/lightningd/json.h
index 4ad44ff0d7b8..2e4230d3b4bb 100644
--- a/lightningd/json.h
+++ b/lightningd/json.h
@@ -12,6 +12,7 @@
# include
struct bitcoin_txid;
+struct channel_id;
struct json_result;
struct pubkey;
struct route_hop;
@@ -50,6 +51,9 @@ void json_add_short_channel_id(struct json_result *response,
const char *fieldname,
const struct short_channel_id *id);
+bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok,
+ struct channel_id *cid);
+
/* JSON serialize a network address for a node */
void json_add_address(struct json_result *response, const char *fieldname,
const struct wireaddr *addr);
diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c
index c068b044b080..3730409dc218 100644
--- a/lightningd/peer_control.c
+++ b/lightningd/peer_control.c
@@ -985,10 +985,60 @@ static const struct json_command listpeers_command = {
};
AUTODATA(json_command, &listpeers_command);
+static struct channel *
+command_find_channel(struct command *cmd,
+ const char *buffer, const jsmntok_t *tok)
+{
+ struct lightningd *ld = cmd->ld;
+ struct channel_id cid;
+ struct channel_id channel_cid;
+ struct short_channel_id scid;
+ struct peer *peer;
+ struct channel *channel;
+
+ if (json_tok_channel_id(buffer, tok, &cid)) {
+ list_for_each(&ld->peers, peer, list) {
+ channel = peer_active_channel(peer);
+ if (!channel)
+ continue;
+ derive_channel_id(&channel_cid,
+ &channel->funding_txid,
+ channel->funding_outnum);
+ if (structeq(&channel_cid, &cid))
+ return channel;
+ }
+ command_fail(cmd,
+ "Channel ID not found: '%.*s'",
+ tok->end - tok->start,
+ buffer + tok->start);
+ return NULL;
+ } else if (json_tok_short_channel_id(buffer, tok, &scid)) {
+ list_for_each(&ld->peers, peer, list) {
+ channel = peer_active_channel(peer);
+ if (!channel)
+ continue;
+ if (channel->scid && channel->scid->u64 == scid.u64)
+ return channel;
+ }
+ command_fail(cmd,
+ "Short channel ID not found: '%.*s'",
+ tok->end - tok->start,
+ buffer + tok->start);
+ return NULL;
+ } else {
+ command_fail(cmd,
+ "Given id is not a channel ID or "
+ "short channel ID: '%.*s'",
+ tok->end - tok->start,
+ buffer + tok->start);
+ return NULL;
+ }
+}
+
static void json_close(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
- jsmntok_t *peertok;
+ jsmntok_t *idtok;
jsmntok_t *timeouttok;
jsmntok_t *forcetok;
struct peer *peer;
@@ -997,18 +1047,13 @@ static void json_close(struct command *cmd,
bool force = false;
if (!json_get_params(cmd, buffer, params,
- "id", &peertok,
+ "id", &idtok,
"?force", &forcetok,
"?timeout", &timeouttok,
NULL)) {
return;
}
- peer = peer_from_json(cmd->ld, buffer, peertok);
- if (!peer) {
- 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,
@@ -1022,8 +1067,16 @@ static void json_close(struct command *cmd,
return;
}
- channel = peer_active_channel(peer);
- if (!channel) {
+ peer = peer_from_json(cmd->ld, buffer, idtok);
+ if (peer)
+ channel = peer_active_channel(peer);
+ else {
+ channel = command_find_channel(cmd, buffer, idtok);
+ if (!channel)
+ return;
+ }
+
+ if (!channel && peer) {
struct uncommitted_channel *uc = peer->uncommitted_channel;
if (uc) {
/* Easy case: peer can simply be forgotten. */
@@ -1045,7 +1098,7 @@ static void json_close(struct command *cmd,
channel->state != CHANNELD_AWAITING_LOCKIN &&
channel->state != CHANNELD_SHUTTING_DOWN &&
channel->state != CLOSINGD_SIGEXCHANGE)
- command_fail(cmd, "Peer is in state %s",
+ command_fail(cmd, "Channel is in state %s",
channel_state_name(channel));
/* If normal or locking in, transition to shutting down
@@ -1071,7 +1124,12 @@ static void json_close(struct command *cmd,
static const struct json_command close_command = {
"close",
json_close,
- "Close the channel with peer {id}"
+ "Close the channel with {id} "
+ "(either peer ID, channel ID, or short channel ID). "
+ "If {force} (default false) is true, force a unilateral close "
+ "after {timeout} seconds (default 30), "
+ "otherwise just schedule a mutual close later and fail after "
+ "timing out."
};
AUTODATA(json_command, &close_command);
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index de3090561de3..34856bb73c74 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -257,6 +257,10 @@ void json_object_start(struct json_result *ptr UNNEEDED, const char *fieldname U
/* Generated stub for json_tok_bool */
bool json_tok_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED)
{ fprintf(stderr, "json_tok_bool called!\n"); abort(); }
+/* Generated stub for json_tok_channel_id */
+bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
+ struct channel_id *cid UNNEEDED)
+{ fprintf(stderr, "json_tok_channel_id called!\n"); abort(); }
/* Generated stub for json_tok_loglevel */
bool json_tok_loglevel(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
enum log_level *level UNNEEDED)