mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
daemon: close command.
This performs a mutual close. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
45c5c83d6f
commit
168ed96b12
@ -254,6 +254,7 @@ static const struct json_command *cmdlist[] = {
|
|||||||
&newhtlc_command,
|
&newhtlc_command,
|
||||||
&fulfillhtlc_command,
|
&fulfillhtlc_command,
|
||||||
&failhtlc_command,
|
&failhtlc_command,
|
||||||
|
&close_command,
|
||||||
/* Developer/debugging options. */
|
/* Developer/debugging options. */
|
||||||
&echo_command,
|
&echo_command,
|
||||||
&rhash_command,
|
&rhash_command,
|
||||||
|
@ -62,4 +62,5 @@ extern const struct json_command newhtlc_command;
|
|||||||
extern const struct json_command fulfillhtlc_command;
|
extern const struct json_command fulfillhtlc_command;
|
||||||
extern const struct json_command failhtlc_command;
|
extern const struct json_command failhtlc_command;
|
||||||
extern const struct json_command mocktime_command;
|
extern const struct json_command mocktime_command;
|
||||||
|
extern const struct json_command close_command;
|
||||||
#endif /* LIGHTNING_DAEMON_JSONRPC_H */
|
#endif /* LIGHTNING_DAEMON_JSONRPC_H */
|
||||||
|
@ -79,6 +79,9 @@ static void config_register_opts(struct lightningd_state *dstate)
|
|||||||
opt_register_arg("--max-anchor-confirms", opt_set_u32, opt_show_u32,
|
opt_register_arg("--max-anchor-confirms", opt_set_u32, opt_show_u32,
|
||||||
&dstate->config.anchor_confirms_max,
|
&dstate->config.anchor_confirms_max,
|
||||||
"Maximum confirmations other side can wait for anchor transaction");
|
"Maximum confirmations other side can wait for anchor transaction");
|
||||||
|
opt_register_arg("--forever-confirms", opt_set_u32, opt_show_u32,
|
||||||
|
&dstate->config.forever_confirms,
|
||||||
|
"Confirmations after which we consider a reorg impossible");
|
||||||
opt_register_arg("--commit-fee", opt_set_u64, opt_show_u64,
|
opt_register_arg("--commit-fee", opt_set_u64, opt_show_u64,
|
||||||
&dstate->config.commitment_fee,
|
&dstate->config.commitment_fee,
|
||||||
"Satoshis to offer for commitment transaction fee");
|
"Satoshis to offer for commitment transaction fee");
|
||||||
@ -120,6 +123,9 @@ static void default_config(struct config *config)
|
|||||||
/* More than 10 confirms seems overkill. */
|
/* More than 10 confirms seems overkill. */
|
||||||
config->anchor_confirms_max = 10;
|
config->anchor_confirms_max = 10;
|
||||||
|
|
||||||
|
/* At some point, you've got to let it go... */
|
||||||
|
config->forever_confirms = 100;
|
||||||
|
|
||||||
/* FIXME: These should float with bitcoind's recommendations! */
|
/* FIXME: These should float with bitcoind's recommendations! */
|
||||||
|
|
||||||
/* Pay hefty fee (10x current suggested minimum). */
|
/* Pay hefty fee (10x current suggested minimum). */
|
||||||
|
@ -26,6 +26,9 @@ struct config {
|
|||||||
/* How long will we accept them waiting? */
|
/* How long will we accept them waiting? */
|
||||||
u32 anchor_confirms_max;
|
u32 anchor_confirms_max;
|
||||||
|
|
||||||
|
/* How many blocks until we stop watching a close commit? */
|
||||||
|
u32 forever_confirms;
|
||||||
|
|
||||||
/* What are we prepared to pay in commitment fee (satoshis). */
|
/* What are we prepared to pay in commitment fee (satoshis). */
|
||||||
u64 commitment_fee;
|
u64 commitment_fee;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "bitcoin/script.h"
|
#include "bitcoin/script.h"
|
||||||
#include "bitcoin/tx.h"
|
#include "bitcoin/tx.h"
|
||||||
|
#include "close_tx.h"
|
||||||
#include "controlled_time.h"
|
#include "controlled_time.h"
|
||||||
#include "find_p2sh_out.h"
|
#include "find_p2sh_out.h"
|
||||||
#include "lightningd.h"
|
#include "lightningd.h"
|
||||||
@ -242,32 +243,32 @@ Pkt *pkt_err(const tal_t *ctx, const char *msg, ...)
|
|||||||
Pkt *pkt_close(const tal_t *ctx, const struct peer *peer)
|
Pkt *pkt_close(const tal_t *ctx, const struct peer *peer)
|
||||||
{
|
{
|
||||||
CloseChannel *c = tal(ctx, CloseChannel);
|
CloseChannel *c = tal(ctx, CloseChannel);
|
||||||
struct signature sig;
|
|
||||||
|
|
||||||
close_channel__init(c);
|
close_channel__init(c);
|
||||||
|
|
||||||
/* FIXME: If we're not connected, we don't create close tx. */
|
c->close_fee = peer->close_tx->fee;
|
||||||
if (!peer->close_tx) {
|
c->sig = signature_to_proto(c, &peer->our_close_sig.sig);
|
||||||
c->close_fee = 0;
|
|
||||||
memset(&sig, 0, sizeof(sig));
|
|
||||||
c->sig = signature_to_proto(c, &sig);
|
|
||||||
} else {
|
|
||||||
c->close_fee = peer->close_tx->fee;
|
|
||||||
peer_sign_mutual_close(peer, peer->close_tx, &sig);
|
|
||||||
c->sig = signature_to_proto(c, &sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
return make_pkt(ctx, PKT__PKT_CLOSE, c);
|
return make_pkt(ctx, PKT__PKT_CLOSE, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pkt *pkt_close_complete(const tal_t *ctx, const struct peer *peer)
|
Pkt *pkt_close_complete(const tal_t *ctx, const struct peer *peer)
|
||||||
{
|
{
|
||||||
FIXME_STUB(peer);
|
CloseChannelComplete *c = tal(ctx, CloseChannelComplete);
|
||||||
|
|
||||||
|
close_channel_complete__init(c);
|
||||||
|
assert(peer->close_tx);
|
||||||
|
c->sig = signature_to_proto(c, &peer->our_close_sig.sig);
|
||||||
|
|
||||||
|
return make_pkt(ctx, PKT__PKT_CLOSE_COMPLETE, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pkt *pkt_close_ack(const tal_t *ctx, const struct peer *peer)
|
Pkt *pkt_close_ack(const tal_t *ctx, const struct peer *peer)
|
||||||
{
|
{
|
||||||
FIXME_STUB(peer);
|
CloseChannelAck *a = tal(ctx, CloseChannelAck);
|
||||||
|
|
||||||
|
close_channel_ack__init(a);
|
||||||
|
return make_pkt(ctx, PKT__PKT_CLOSE_ACK, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pkt *pkt_err_unexpected(const tal_t *ctx, const Pkt *pkt)
|
Pkt *pkt_err_unexpected(const tal_t *ctx, const Pkt *pkt)
|
||||||
@ -758,15 +759,57 @@ Pkt *accept_pkt_update_signature(const tal_t *ctx,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool peer_sign_close_tx(struct peer *peer, const Signature *theirs)
|
||||||
|
{
|
||||||
|
struct bitcoin_signature theirsig;
|
||||||
|
|
||||||
|
/* We never sign twice! */
|
||||||
|
assert(peer->close_tx->input[0].script_length == 0);
|
||||||
|
|
||||||
|
theirsig.stype = SIGHASH_ALL;
|
||||||
|
if (!proto_to_signature(theirs, &theirsig.sig))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Their sig + ours should sign the close tx. */
|
||||||
|
if (!check_2of2_sig(peer->dstate->secpctx,
|
||||||
|
peer->close_tx, 0,
|
||||||
|
peer->anchor.redeemscript,
|
||||||
|
tal_count(peer->anchor.redeemscript),
|
||||||
|
&peer->them.commitkey, &peer->us.commitkey,
|
||||||
|
&theirsig, &peer->our_close_sig))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Complete the close_tx, using signatures. */
|
||||||
|
peer->close_tx->input[0].script
|
||||||
|
= scriptsig_p2sh_2of2(peer->close_tx,
|
||||||
|
&theirsig, &peer->our_close_sig,
|
||||||
|
&peer->them.commitkey,
|
||||||
|
&peer->us.commitkey);
|
||||||
|
peer->close_tx->input[0].script_length
|
||||||
|
= tal_count(peer->close_tx->input[0].script);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Pkt *accept_pkt_close(const tal_t *ctx, struct peer *peer, const Pkt *pkt)
|
Pkt *accept_pkt_close(const tal_t *ctx, struct peer *peer, const Pkt *pkt)
|
||||||
{
|
{
|
||||||
FIXME_STUB(peer);
|
const CloseChannel *c = pkt->close;
|
||||||
|
|
||||||
|
/* FIXME: Don't accept tiny close fee! */
|
||||||
|
if (!peer_create_close_tx(peer, c->close_fee))
|
||||||
|
return pkt_err(ctx, "Invalid close fee");
|
||||||
|
|
||||||
|
if (!peer_sign_close_tx(peer, c->sig))
|
||||||
|
return pkt_err(ctx, "Invalid signature");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pkt *accept_pkt_close_complete(const tal_t *ctx,
|
Pkt *accept_pkt_close_complete(const tal_t *ctx,
|
||||||
struct peer *peer, const Pkt *pkt)
|
struct peer *peer, const Pkt *pkt)
|
||||||
{
|
{
|
||||||
FIXME_STUB(peer);
|
const CloseChannelComplete *c = pkt->close_complete;
|
||||||
|
if (!peer_sign_close_tx(peer, c->sig))
|
||||||
|
return pkt_err(ctx, "Invalid signature");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pkt *accept_pkt_simultaneous_close(const tal_t *ctx,
|
Pkt *accept_pkt_simultaneous_close(const tal_t *ctx,
|
||||||
@ -776,7 +819,8 @@ Pkt *accept_pkt_simultaneous_close(const tal_t *ctx,
|
|||||||
FIXME_STUB(peer);
|
FIXME_STUB(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: Since this packet is empty, is it worth having? */
|
||||||
Pkt *accept_pkt_close_ack(const tal_t *ctx, struct peer *peer, const Pkt *pkt)
|
Pkt *accept_pkt_close_ack(const tal_t *ctx, struct peer *peer, const Pkt *pkt)
|
||||||
{
|
{
|
||||||
FIXME_STUB(peer);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
183
daemon/peer.c
183
daemon/peer.c
@ -293,6 +293,9 @@ static void peer_disconnect(struct io_conn *conn, struct peer *peer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: Try to reconnect. */
|
/* FIXME: Try to reconnect. */
|
||||||
|
if (peer->cond == PEER_CLOSING
|
||||||
|
|| peer->cond == PEER_CLOSED)
|
||||||
|
return;
|
||||||
|
|
||||||
state(peer, peer, CMD_CLOSE, NULL, &outpkt, &broadcast);
|
state(peer, peer, CMD_CLOSE, NULL, &outpkt, &broadcast);
|
||||||
/* Can't send packet, so ignore it. */
|
/* Can't send packet, so ignore it. */
|
||||||
@ -339,6 +342,7 @@ static struct peer *new_peer(struct lightningd_state *dstate,
|
|||||||
peer->num_htlcs = 0;
|
peer->num_htlcs = 0;
|
||||||
peer->close_tx = NULL;
|
peer->close_tx = NULL;
|
||||||
peer->cstate = NULL;
|
peer->cstate = NULL;
|
||||||
|
peer->close_watch_timeout = NULL;
|
||||||
|
|
||||||
/* If we free peer, conn should be closed, but can't be freed
|
/* If we free peer, conn should be closed, but can't be freed
|
||||||
* immediately so don't make peer a parent. */
|
* immediately so don't make peer a parent. */
|
||||||
@ -597,6 +601,38 @@ static bool txmatch(const struct bitcoin_tx *txa, const struct bitcoin_tx *txb)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_mutual_close(const struct bitcoin_tx *tx,
|
||||||
|
const struct bitcoin_tx *close_tx)
|
||||||
|
{
|
||||||
|
varint_t i;
|
||||||
|
|
||||||
|
/* Haven't created mutual close yet? This isn't one then. */
|
||||||
|
if (!close_tx)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* We know it spends anchor, but do txouts match? */
|
||||||
|
if (tx->output_count != close_tx->output_count)
|
||||||
|
return false;
|
||||||
|
for (i = 0; i < tx->output_count; i++) {
|
||||||
|
if (tx->output[i].amount != close_tx->output[i].amount)
|
||||||
|
return false;
|
||||||
|
if (tx->output[i].script_length
|
||||||
|
!= close_tx->output[i].script_length)
|
||||||
|
return false;
|
||||||
|
if (memcmp(tx->output[i].script, close_tx->output[i].script,
|
||||||
|
tx->output[i].script_length) != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_depth_cb(struct peer *peer, int depth)
|
||||||
|
{
|
||||||
|
if (depth >= peer->dstate->config.forever_confirms) {
|
||||||
|
state_event(peer, BITCOIN_CLOSE_DONE, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* We assume the tx is valid! Don't do a blockchain.info and feed this
|
/* We assume the tx is valid! Don't do a blockchain.info and feed this
|
||||||
* invalid transactions! */
|
* invalid transactions! */
|
||||||
static void anchor_spent(struct peer *peer,
|
static void anchor_spent(struct peer *peer,
|
||||||
@ -609,6 +645,8 @@ static void anchor_spent(struct peer *peer,
|
|||||||
idata.btc = (struct bitcoin_event *)tx;
|
idata.btc = (struct bitcoin_event *)tx;
|
||||||
if (txmatch(tx, peer->them.commit))
|
if (txmatch(tx, peer->them.commit))
|
||||||
state_event(peer, w->theyspent, &idata);
|
state_event(peer, w->theyspent, &idata);
|
||||||
|
else if (is_mutual_close(tx, peer->close_tx))
|
||||||
|
add_close_tx_watch(peer, tx, close_depth_cb);
|
||||||
else
|
else
|
||||||
state_event(peer, w->otherspent, &idata);
|
state_event(peer, w->otherspent, &idata);
|
||||||
}
|
}
|
||||||
@ -656,29 +694,82 @@ void peer_watch_tx(struct peer *peer,
|
|||||||
FIXME_STUB(peer);
|
FIXME_STUB(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool peer_create_close_tx(struct peer *peer, u64 fee_satoshis)
|
||||||
|
{
|
||||||
|
struct channel_state cstate;
|
||||||
|
|
||||||
|
assert(!peer->close_tx);
|
||||||
|
|
||||||
|
/* We don't need a deep copy here, just fee levels. */
|
||||||
|
cstate = *peer->cstate;
|
||||||
|
if (!adjust_fee(peer->us.offer_anchor == CMD_OPEN_WITH_ANCHOR,
|
||||||
|
peer->anchor.satoshis,
|
||||||
|
fee_satoshis,
|
||||||
|
&cstate.a, &cstate.b))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
log_debug(peer->log,
|
||||||
|
"creating close-tx: to %02x%02x%02x%02x/%02x%02x%02x%02x, amounts %u/%u",
|
||||||
|
peer->us.finalkey.der[0], peer->us.finalkey.der[1],
|
||||||
|
peer->us.finalkey.der[2], peer->us.finalkey.der[3],
|
||||||
|
peer->them.finalkey.der[0], peer->them.finalkey.der[1],
|
||||||
|
peer->them.finalkey.der[2], peer->them.finalkey.der[3],
|
||||||
|
cstate.a.pay_msat / 1000,
|
||||||
|
cstate.b.pay_msat / 1000);
|
||||||
|
|
||||||
|
peer->close_tx = create_close_tx(peer->dstate->secpctx, peer,
|
||||||
|
&peer->us.finalkey,
|
||||||
|
&peer->them.finalkey,
|
||||||
|
&peer->anchor.txid,
|
||||||
|
peer->anchor.index,
|
||||||
|
peer->anchor.satoshis,
|
||||||
|
cstate.a.pay_msat / 1000,
|
||||||
|
cstate.b.pay_msat / 1000);
|
||||||
|
|
||||||
|
peer->our_close_sig.stype = SIGHASH_ALL;
|
||||||
|
peer_sign_mutual_close(peer, peer->close_tx, &peer->our_close_sig.sig);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void send_close_timeout(struct peer *peer)
|
static void send_close_timeout(struct peer *peer)
|
||||||
{
|
{
|
||||||
|
/* FIXME: Remove any close_tx watches! */
|
||||||
state_event(peer, INPUT_CLOSE_COMPLETE_TIMEOUT, NULL);
|
state_event(peer, INPUT_CLOSE_COMPLETE_TIMEOUT, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void peer_watch_close(struct peer *peer,
|
void peer_watch_close(struct peer *peer,
|
||||||
enum state_input done, enum state_input timedout)
|
enum state_input done, enum state_input timedout)
|
||||||
{
|
{
|
||||||
/* We save some work by assuming this. */
|
/* We save some work by assuming these. */
|
||||||
assert(timedout == INPUT_CLOSE_COMPLETE_TIMEOUT);
|
assert(done == BITCOIN_CLOSE_DONE);
|
||||||
|
|
||||||
/* FIXME: We didn't send CLOSE, so timeout immediately */
|
/* FIXME: Dynamic closing fee! */
|
||||||
|
if (!peer->close_tx)
|
||||||
|
peer_create_close_tx(peer, peer->dstate->config.closing_fee);
|
||||||
|
|
||||||
|
/* FIXME: We can't send CLOSE, so timeout immediately */
|
||||||
if (!peer->conn) {
|
if (!peer->conn) {
|
||||||
(void)send_close_timeout;
|
assert(timedout == INPUT_CLOSE_COMPLETE_TIMEOUT);
|
||||||
/* FIXME: oneshot_timeout(peer->dstate, peer, 0, send_close_timeout, peer); */
|
oneshot_timeout(peer->dstate, peer, 0,
|
||||||
|
send_close_timeout, peer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FIXME_STUB(peer);
|
/* Give them a reasonable time to respond. */
|
||||||
|
/* FIXME: config? */
|
||||||
|
if (timedout != INPUT_NONE) {
|
||||||
|
assert(timedout == INPUT_CLOSE_COMPLETE_TIMEOUT);
|
||||||
|
peer->close_watch_timeout
|
||||||
|
= oneshot_timeout(peer->dstate, peer, 120,
|
||||||
|
send_close_timeout, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* anchor_spent will get called, we match against close_tx there. */
|
||||||
}
|
}
|
||||||
void peer_unwatch_close_timeout(struct peer *peer, enum state_input timedout)
|
void peer_unwatch_close_timeout(struct peer *peer, enum state_input timedout)
|
||||||
{
|
{
|
||||||
FIXME_STUB(peer);
|
assert(peer->close_watch_timeout);
|
||||||
|
peer->close_watch_timeout = tal_free(peer->close_watch_timeout);
|
||||||
}
|
}
|
||||||
bool peer_watch_our_htlc_outputs(struct peer *peer,
|
bool peer_watch_our_htlc_outputs(struct peer *peer,
|
||||||
const struct bitcoin_tx *tx,
|
const struct bitcoin_tx *tx,
|
||||||
@ -767,36 +858,9 @@ bool committed_to_htlcs(const struct peer *peer)
|
|||||||
const struct bitcoin_tx *bitcoin_close(const tal_t *ctx,
|
const struct bitcoin_tx *bitcoin_close(const tal_t *ctx,
|
||||||
const struct peer *peer)
|
const struct peer *peer)
|
||||||
{
|
{
|
||||||
#if 0
|
/* Must be signed! */
|
||||||
struct bitcoin_tx *close_tx;
|
assert(peer->close_tx->input[0].script_length != 0);
|
||||||
u8 *redeemscript;
|
return peer->close_tx;
|
||||||
|
|
||||||
close_tx = create_close_tx(ctx, peer->us.openpkt, peer->them.openpkt,
|
|
||||||
peer->anchorpkt,
|
|
||||||
peer->cstate.a.pay_msat / 1000,
|
|
||||||
peer->cstate.b.pay_msat / 1000);
|
|
||||||
|
|
||||||
/* This is what the anchor pays to. */
|
|
||||||
redeemscript = bitcoin_redeem_2of2(close_tx, &peer->us.commitkey,
|
|
||||||
&peer->them.commitkey);
|
|
||||||
|
|
||||||
/* Combined signatures must validate correctly. */
|
|
||||||
if (!check_2of2_sig(close_tx, 0, redeemscript, tal_count(redeemscript),
|
|
||||||
&peer->us.finalkey, &peer->them.finalkey,
|
|
||||||
&peer->us.closesig, &peer->them.closesig))
|
|
||||||
fatal("bitcoin_close signature failed");
|
|
||||||
|
|
||||||
/* Create p2sh input for close_tx */
|
|
||||||
close_tx->input[0].script = scriptsig_p2sh_2of2(close_tx,
|
|
||||||
&peer->us.closesig,
|
|
||||||
&peer->them.closesig,
|
|
||||||
&peer->us.finalkey,
|
|
||||||
&peer->them.finalkey);
|
|
||||||
close_tx->input[0].script_length = tal_count(close_tx->input[0].script);
|
|
||||||
|
|
||||||
return close_tx;
|
|
||||||
#endif
|
|
||||||
FIXME_STUB(peer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a bitcoin spend tx (to spend our commit's outputs) */
|
/* Create a bitcoin spend tx (to spend our commit's outputs) */
|
||||||
@ -1448,3 +1512,48 @@ const struct json_command failhtlc_command = {
|
|||||||
"Fail htlc proposed by {id} which has redeem hash {rhash}",
|
"Fail htlc proposed by {id} which has redeem hash {rhash}",
|
||||||
"Returns an empty result on success"
|
"Returns an empty result on success"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void json_close(struct command *cmd,
|
||||||
|
const char *buffer, const jsmntok_t *params)
|
||||||
|
{
|
||||||
|
struct peer *peer;
|
||||||
|
jsmntok_t *idtok;
|
||||||
|
struct pubkey id;
|
||||||
|
|
||||||
|
json_get_params(buffer, params,
|
||||||
|
"id", &idtok,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!idtok) {
|
||||||
|
command_fail(cmd, "Need id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pubkey_from_hexstr(cmd->dstate->secpctx,
|
||||||
|
buffer + idtok->start,
|
||||||
|
idtok->end - idtok->start, &id)) {
|
||||||
|
command_fail(cmd, "Not a valid id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
peer = find_peer(cmd->dstate, &id);
|
||||||
|
if (!peer) {
|
||||||
|
command_fail(cmd, "Could not find peer with that id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (peer->cond == PEER_CLOSING) {
|
||||||
|
command_fail(cmd, "Peer is already closing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlike other things, CMD_CLOSE is always valid. */
|
||||||
|
log_debug(peer->log, "Sending CMD_CLOSE");
|
||||||
|
state_event(peer, CMD_CLOSE, NULL);
|
||||||
|
command_success(cmd, null_response(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct json_command close_command = {
|
||||||
|
"close",
|
||||||
|
json_close,
|
||||||
|
"Close the channel with peer {id}",
|
||||||
|
"Returns an empty result on success"
|
||||||
|
};
|
||||||
|
@ -104,8 +104,9 @@ struct peer {
|
|||||||
/* Number of HTLC updates (== number of previous commit txs) */
|
/* Number of HTLC updates (== number of previous commit txs) */
|
||||||
u64 num_htlcs;
|
u64 num_htlcs;
|
||||||
|
|
||||||
/* Closing tx, once we've generated it */
|
/* Closing tx and signature once we've generated it */
|
||||||
struct bitcoin_tx *close_tx;
|
struct bitcoin_tx *close_tx;
|
||||||
|
struct bitcoin_signature our_close_sig;
|
||||||
|
|
||||||
/* Current ongoing packetflow */
|
/* Current ongoing packetflow */
|
||||||
struct io_data *io_data;
|
struct io_data *io_data;
|
||||||
@ -116,6 +117,9 @@ struct peer {
|
|||||||
/* Things we're watching for (see watches.c) */
|
/* Things we're watching for (see watches.c) */
|
||||||
struct list_head watches;
|
struct list_head watches;
|
||||||
|
|
||||||
|
/* Timeout for close_watch. */
|
||||||
|
struct oneshot *close_watch_timeout;
|
||||||
|
|
||||||
/* Private keys for dealing with this peer. */
|
/* Private keys for dealing with this peer. */
|
||||||
struct peer_secrets *secrets;
|
struct peer_secrets *secrets;
|
||||||
|
|
||||||
@ -135,4 +139,6 @@ void make_commit_txs(const tal_t *ctx,
|
|||||||
void peer_add_htlc_expiry(struct peer *peer,
|
void peer_add_htlc_expiry(struct peer *peer,
|
||||||
const struct abs_locktime *expiry);
|
const struct abs_locktime *expiry);
|
||||||
|
|
||||||
|
bool peer_create_close_tx(struct peer *peer, u64 fee_satoshis);
|
||||||
|
|
||||||
#endif /* LIGHTNING_DAEMON_PEER_H */
|
#endif /* LIGHTNING_DAEMON_PEER_H */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#! /bin/sh -e
|
#! /bin/sh -ex
|
||||||
|
|
||||||
# We steal the test-cli scripts.
|
# We steal the test-cli scripts.
|
||||||
cd test-cli
|
cd test-cli
|
||||||
@ -138,8 +138,33 @@ sleep 1
|
|||||||
# Back to how we were before.
|
# Back to how we were before.
|
||||||
check_status 949999000 49000000 "" 0 1000000 ""
|
check_status 949999000 49000000 "" 0 1000000 ""
|
||||||
|
|
||||||
|
$LCLI1 close $ID2
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
|
# They should be waiting for close.
|
||||||
|
$LCLI1 getpeers | tr -s '\012\011 ' ' ' | fgrep '"STATE_CLOSE_WAIT_CLOSE"'
|
||||||
|
$LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep '"STATE_CLOSE_WAIT_CLOSE"'
|
||||||
|
|
||||||
|
# Give it 99 blocks.
|
||||||
|
$CLI generate 99
|
||||||
|
|
||||||
|
# Make sure they saw it!
|
||||||
|
$LCLI1 dev-mocktime $(($EXPIRY + 32))
|
||||||
|
$LCLI2 dev-mocktime $(($EXPIRY + 32))
|
||||||
|
sleep 1
|
||||||
|
$LCLI1 getpeers | tr -s '\012\011 ' ' ' | fgrep '"STATE_CLOSE_WAIT_CLOSE"'
|
||||||
|
$LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep '"STATE_CLOSE_WAIT_CLOSE"'
|
||||||
|
|
||||||
|
# Now the final one.
|
||||||
|
$CLI generate 1
|
||||||
|
$LCLI1 dev-mocktime $(($EXPIRY + 33))
|
||||||
|
$LCLI2 dev-mocktime $(($EXPIRY + 33))
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$LCLI1 getpeers | tr -s '\012\011 ' ' ' | fgrep '"peers" : [ ]'
|
||||||
|
$LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep '"peers" : [ ]'
|
||||||
|
|
||||||
$LCLI1 stop
|
$LCLI1 stop
|
||||||
$LCLI2 stop
|
$LCLI2 stop
|
||||||
scripts/shutdown.sh
|
scripts/shutdown.sh
|
||||||
|
@ -167,6 +167,24 @@ void add_commit_tx_watch_(struct peer *peer,
|
|||||||
* watch anything else. */
|
* watch anything else. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cb_no_arg(struct peer *peer, int depth, void *vcb)
|
||||||
|
{
|
||||||
|
void (*cb)(struct peer *peer, int depth) = vcb;
|
||||||
|
cb(peer, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_close_tx_watch(struct peer *peer,
|
||||||
|
const struct bitcoin_tx *tx,
|
||||||
|
void (*cb)(struct peer *peer, int depth))
|
||||||
|
{
|
||||||
|
struct sha256_double txid;
|
||||||
|
bitcoin_txid(tx, &txid);
|
||||||
|
insert_txwatch(peer, peer->dstate, peer, &txid, cb_no_arg, cb);
|
||||||
|
|
||||||
|
/* We are already watching the anchor txo, so we don't need to
|
||||||
|
* watch anything else. */
|
||||||
|
}
|
||||||
|
|
||||||
static void tx_watched_inputs(struct lightningd_state *dstate,
|
static void tx_watched_inputs(struct lightningd_state *dstate,
|
||||||
const struct bitcoin_tx *tx, void *unused)
|
const struct bitcoin_tx *tx, void *unused)
|
||||||
{
|
{
|
||||||
|
@ -94,5 +94,9 @@ void add_commit_tx_watch_(struct peer *peer,
|
|||||||
int depth), \
|
int depth), \
|
||||||
(cbdata))
|
(cbdata))
|
||||||
|
|
||||||
|
void add_close_tx_watch(struct peer *peer,
|
||||||
|
const struct bitcoin_tx *tx,
|
||||||
|
void (*cb)(struct peer *peer, int depth));
|
||||||
|
|
||||||
void setup_watch_timer(struct lightningd_state *dstate);
|
void setup_watch_timer(struct lightningd_state *dstate);
|
||||||
#endif /* LIGHTNING_DAEMON_WATCH_H */
|
#endif /* LIGHTNING_DAEMON_WATCH_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user