mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +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,
|
||||
&fulfillhtlc_command,
|
||||
&failhtlc_command,
|
||||
&close_command,
|
||||
/* Developer/debugging options. */
|
||||
&echo_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 failhtlc_command;
|
||||
extern const struct json_command mocktime_command;
|
||||
extern const struct json_command close_command;
|
||||
#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,
|
||||
&dstate->config.anchor_confirms_max,
|
||||
"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,
|
||||
&dstate->config.commitment_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. */
|
||||
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! */
|
||||
|
||||
/* Pay hefty fee (10x current suggested minimum). */
|
||||
|
@ -26,6 +26,9 @@ struct config {
|
||||
/* How long will we accept them waiting? */
|
||||
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). */
|
||||
u64 commitment_fee;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "bitcoin/script.h"
|
||||
#include "bitcoin/tx.h"
|
||||
#include "close_tx.h"
|
||||
#include "controlled_time.h"
|
||||
#include "find_p2sh_out.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)
|
||||
{
|
||||
CloseChannel *c = tal(ctx, CloseChannel);
|
||||
struct signature sig;
|
||||
|
||||
close_channel__init(c);
|
||||
|
||||
/* FIXME: If we're not connected, we don't create close tx. */
|
||||
if (!peer->close_tx) {
|
||||
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);
|
||||
}
|
||||
c->close_fee = peer->close_tx->fee;
|
||||
c->sig = signature_to_proto(c, &peer->our_close_sig.sig);
|
||||
|
||||
return make_pkt(ctx, PKT__PKT_CLOSE, c);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -758,15 +759,57 @@ Pkt *accept_pkt_update_signature(const tal_t *ctx,
|
||||
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)
|
||||
{
|
||||
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,
|
||||
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,
|
||||
@ -776,7 +819,8 @@ Pkt *accept_pkt_simultaneous_close(const tal_t *ctx,
|
||||
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)
|
||||
{
|
||||
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. */
|
||||
if (peer->cond == PEER_CLOSING
|
||||
|| peer->cond == PEER_CLOSED)
|
||||
return;
|
||||
|
||||
state(peer, peer, CMD_CLOSE, NULL, &outpkt, &broadcast);
|
||||
/* 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->close_tx = NULL;
|
||||
peer->cstate = NULL;
|
||||
peer->close_watch_timeout = NULL;
|
||||
|
||||
/* If we free peer, conn should be closed, but can't be freed
|
||||
* 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;
|
||||
}
|
||||
|
||||
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
|
||||
* invalid transactions! */
|
||||
static void anchor_spent(struct peer *peer,
|
||||
@ -609,6 +645,8 @@ static void anchor_spent(struct peer *peer,
|
||||
idata.btc = (struct bitcoin_event *)tx;
|
||||
if (txmatch(tx, peer->them.commit))
|
||||
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
|
||||
state_event(peer, w->otherspent, &idata);
|
||||
}
|
||||
@ -656,29 +694,82 @@ void peer_watch_tx(struct peer *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)
|
||||
{
|
||||
/* FIXME: Remove any close_tx watches! */
|
||||
state_event(peer, INPUT_CLOSE_COMPLETE_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
void peer_watch_close(struct peer *peer,
|
||||
enum state_input done, enum state_input timedout)
|
||||
{
|
||||
/* We save some work by assuming this. */
|
||||
assert(timedout == INPUT_CLOSE_COMPLETE_TIMEOUT);
|
||||
/* We save some work by assuming these. */
|
||||
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) {
|
||||
(void)send_close_timeout;
|
||||
/* FIXME: oneshot_timeout(peer->dstate, peer, 0, send_close_timeout, peer); */
|
||||
assert(timedout == INPUT_CLOSE_COMPLETE_TIMEOUT);
|
||||
oneshot_timeout(peer->dstate, peer, 0,
|
||||
send_close_timeout, peer);
|
||||
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)
|
||||
{
|
||||
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,
|
||||
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 peer *peer)
|
||||
{
|
||||
#if 0
|
||||
struct bitcoin_tx *close_tx;
|
||||
u8 *redeemscript;
|
||||
|
||||
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);
|
||||
/* Must be signed! */
|
||||
assert(peer->close_tx->input[0].script_length != 0);
|
||||
return peer->close_tx;
|
||||
}
|
||||
|
||||
/* 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}",
|
||||
"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) */
|
||||
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_signature our_close_sig;
|
||||
|
||||
/* Current ongoing packetflow */
|
||||
struct io_data *io_data;
|
||||
@ -116,6 +117,9 @@ struct peer {
|
||||
/* Things we're watching for (see watches.c) */
|
||||
struct list_head watches;
|
||||
|
||||
/* Timeout for close_watch. */
|
||||
struct oneshot *close_watch_timeout;
|
||||
|
||||
/* Private keys for dealing with this peer. */
|
||||
struct peer_secrets *secrets;
|
||||
|
||||
@ -135,4 +139,6 @@ void make_commit_txs(const tal_t *ctx,
|
||||
void peer_add_htlc_expiry(struct peer *peer,
|
||||
const struct abs_locktime *expiry);
|
||||
|
||||
bool peer_create_close_tx(struct peer *peer, u64 fee_satoshis);
|
||||
|
||||
#endif /* LIGHTNING_DAEMON_PEER_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
#! /bin/sh -e
|
||||
#! /bin/sh -ex
|
||||
|
||||
# We steal the test-cli scripts.
|
||||
cd test-cli
|
||||
@ -138,8 +138,33 @@ sleep 1
|
||||
# Back to how we were before.
|
||||
check_status 949999000 49000000 "" 0 1000000 ""
|
||||
|
||||
$LCLI1 close $ID2
|
||||
|
||||
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
|
||||
$LCLI2 stop
|
||||
scripts/shutdown.sh
|
||||
|
@ -167,6 +167,24 @@ void add_commit_tx_watch_(struct peer *peer,
|
||||
* 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,
|
||||
const struct bitcoin_tx *tx, void *unused)
|
||||
{
|
||||
|
@ -94,5 +94,9 @@ void add_commit_tx_watch_(struct peer *peer,
|
||||
int depth), \
|
||||
(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);
|
||||
#endif /* LIGHTNING_DAEMON_WATCH_H */
|
||||
|
Loading…
Reference in New Issue
Block a user