Browse Source

daemon: close command.

This performs a mutual close.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
168ed96b12
  1. 1
      daemon/jsonrpc.c
  2. 1
      daemon/jsonrpc.h
  3. 6
      daemon/lightningd.c
  4. 3
      daemon/lightningd.h
  5. 74
      daemon/packets.c
  6. 183
      daemon/peer.c
  7. 8
      daemon/peer.h
  8. 27
      daemon/test/test.sh
  9. 18
      daemon/watch.c
  10. 4
      daemon/watch.h

1
daemon/jsonrpc.c

@ -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,

1
daemon/jsonrpc.h

@ -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 */

6
daemon/lightningd.c

@ -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). */

3
daemon/lightningd.h

@ -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;

74
daemon/packets.c

@ -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. */
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; c->close_fee = peer->close_tx->fee;
peer_sign_mutual_close(peer, peer->close_tx, &sig); c->sig = signature_to_proto(c, &peer->our_close_sig.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

@ -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: Dynamic closing fee! */
if (!peer->close_tx)
peer_create_close_tx(peer, peer->dstate->config.closing_fee);
/* FIXME: We didn't send CLOSE, so timeout immediately */ /* 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"
};

8
daemon/peer.h

@ -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 */

27
daemon/test/test.sh

@ -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
# 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 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

18
daemon/watch.c

@ -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)
{ {

4
daemon/watch.h

@ -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…
Cancel
Save