Browse Source

daemon: allow commands during commit.

There's no real reason to avoid commands for the next commit; this has
the benefit that we can remove the infrastructure to queue commands.
The only exceptions are the commit command and the opening phase.

We still only allow one commit at a time, but that's mainly run off a
timer which can try again later.  For the JSONRPC API used for
testing, we can simply fail the commit if one is in progress.

For opening we add an explicit peer_open_complete() call in place of
using the command infrastructure.

Commands are now outside the state machine altogether: we simply have
it return the new state instead of the command status.  The JSONRPC
functions can also now run commands directly.

This removes the idea of "peercond" as well: you can simply examine
the states to determine whether an input is valid.  There are
fine-grained helpers for this now, too.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
4110376e87
  1. 520
      daemon/peer.c
  2. 19
      daemon/peer.h
  3. 13
      daemon/test/test.sh
  4. 20
      names.c
  5. 2
      names.h
  6. 207
      state.c
  7. 61
      state.h
  8. 19
      state_types.h

520
daemon/peer.c

@ -90,132 +90,135 @@ static struct json_result *null_response(const tal_t *ctx)
return response; return response;
} }
static void complete_json_command(struct command *jsoncmd, /* FIXME: reorder to avoid predecls. */
enum command_status status) static bool peer_uncommitted_changes(const struct peer *peer);
static void state_event(struct peer *peer,
const enum state_input input,
const union input *idata);
void peer_update_complete(struct peer *peer, const char *problem)
{ {
if (jsoncmd) { if (!problem) {
if (status == CMD_FAIL) log_debug(peer->log, "peer_update_complete");
/* FIXME: y'know, details. */ if (peer->commit_jsoncmd)
command_fail(jsoncmd, "Failed"); command_success(peer->commit_jsoncmd,
else { null_response(peer->commit_jsoncmd));
assert(status == CMD_SUCCESS); } else {
command_success(jsoncmd, null_response(jsoncmd)); log_unusual(peer->log, "peer_update_complete failed: %s",
} problem);
if (peer->commit_jsoncmd)
command_fail(peer->commit_jsoncmd, "%s", problem);
} }
}
static void peer_cmd_complete(struct peer *peer, enum command_status status) /* Simply unset it: it will free itself. */
{ peer->commit_jsoncmd = NULL;
complete_json_command(peer->curr_cmd.jsoncmd, status);
peer->curr_cmd.jsoncmd = NULL; /* Have we got more changes in the meantime? */
peer->curr_cmd.cmd = INPUT_NONE; if (peer_uncommitted_changes(peer)) {
log_debug(peer->log, "peer_update_complete: more changes!");
remote_changes_pending(peer);
}
} }
static void set_current_command(struct peer *peer, void peer_open_complete(struct peer *peer, const char *problem)
const enum state_input input,
void *idata,
struct command *jsoncmd)
{ {
assert(peer->curr_cmd.cmd == INPUT_NONE); if (problem)
assert(input != INPUT_NONE); log_unusual(peer->log, "peer open failed: %s", problem);
else
peer->curr_cmd.cmd = input; log_debug(peer->log, "peer open complete");
/* This is a union, so assign to any member. */
peer->curr_cmd.cmddata.pkt = idata;
peer->curr_cmd.jsoncmd = jsoncmd;
} }
void set_peer_state(struct peer *peer, enum state newstate, const char *caller) static void set_peer_state(struct peer *peer, enum state newstate,
const char *caller)
{ {
log_debug(peer->log, "%s: %s => %s", caller, log_debug(peer->log, "%s: %s => %s", caller,
state_name(peer->state), state_name(newstate)); state_name(peer->state), state_name(newstate));
peer->state = newstate; peer->state = newstate;
} }
/* FIXME: Flatten into do_commit. */
static void command_send_commit(struct peer *peer, struct command *jsoncmd) static void command_send_commit(struct peer *peer, struct command *jsoncmd)
{ {
assert(peer->curr_cmd.cmd == INPUT_NONE); assert(state_can_commit(peer->state));
assert(!peer->commit_jsoncmd);
/* Commands should still be blocked during this! */
assert(peer->state != STATE_CLEARING_COMMITTING); peer->commit_jsoncmd = jsoncmd;
if (peer->state == STATE_CLEARING) { if (peer->state == STATE_CLEARING) {
peer->cond = PEER_BUSY;
peer->curr_cmd.jsoncmd = jsoncmd;
queue_pkt_commit(peer); queue_pkt_commit(peer);
set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__); set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__);
} else { } else {
/* You can send commands if closing; peer->cond == CLOSING */ /* You can send commands if closing; peer->cond == CLOSING */
assert(!state_is_closing(peer->state)); assert(peer->state == STATE_NORMAL);
set_current_command(peer, CMD_SEND_COMMIT, NULL, jsoncmd); state_event(peer, CMD_SEND_COMMIT, NULL);
} }
} }
static void set_htlc_command(struct peer *peer, static bool command_htlc_fail(struct peer *peer, u64 id, struct command *jsoncmd)
struct command *jsoncmd,
enum state_input cmd,
const union htlc_staging *stage)
{ {
/* FIXME: memleak! */ if (!state_can_remove_htlc(peer->state)) {
/* FIXME: Get rid of struct htlc_progress */ if (jsoncmd)
struct htlc_progress *progress = tal(peer, struct htlc_progress); command_fail(jsoncmd,
"htlc_fail not possible in state %s",
progress->stage = *stage; state_name(peer->state));
set_current_command(peer, cmd, progress, jsoncmd); return false;
} }
static void command_htlc_fail(struct peer *peer, u64 id, struct command *jsoncmd)
{
assert(peer->curr_cmd.cmd == INPUT_NONE);
/* Commands should still be blocked during this! */
assert(peer->state != STATE_CLEARING_COMMITTING);
if (peer->state == STATE_CLEARING) { if (peer->state == STATE_CLEARING) {
queue_pkt_htlc_fail(peer, id); queue_pkt_htlc_fail(peer, id);
complete_json_command(jsoncmd, CMD_SUCCESS);
} else { } else {
union htlc_staging stage; union input idata;
struct htlc_progress prog;
/* You can't send commands if closing; peer->cond == CLOSING */ prog.stage.fail.fail = HTLC_FAIL;
assert(!state_is_closing(peer->state)); prog.stage.fail.id = id;
/* FIXME: get rid of htlc_progress, just use htlc_staging. */
stage.fail.fail = HTLC_FAIL; idata.htlc_prog = &prog;
stage.fail.id = id; state_event(peer, CMD_SEND_HTLC_FAIL, &idata);
set_htlc_command(peer, jsoncmd, CMD_SEND_HTLC_FAIL, &stage);
} }
if (jsoncmd)
command_success(jsoncmd, null_response(jsoncmd));
return true;
} }
static void command_htlc_fulfill(struct peer *peer, /* FIXME: Flatten into json_fulfillhtlc. */
static bool command_htlc_fulfill(struct peer *peer,
u64 id, u64 id,
const struct sha256 *r, const struct sha256 *r,
struct command *jsoncmd) struct command *jsoncmd)
{ {
assert(peer->curr_cmd.cmd == INPUT_NONE); if (!state_can_remove_htlc(peer->state)) {
if (jsoncmd)
command_fail(jsoncmd,
"htlc_fulfill not possible in state %s",
state_name(peer->state));
return false;
}
/* Commands should still be blocked during this! */ /* Commands should still be blocked during this! */
assert(peer->state != STATE_CLEARING_COMMITTING); assert(peer->state != STATE_CLEARING_COMMITTING);
if (peer->state == STATE_CLEARING) { if (peer->state == STATE_CLEARING) {
queue_pkt_htlc_fulfill(peer, id, r); queue_pkt_htlc_fulfill(peer, id, r);
complete_json_command(jsoncmd, CMD_SUCCESS);
} else { } else {
union htlc_staging stage; union input idata;
struct htlc_progress prog;
/* You can't send commands if closing; peer->cond == CLOSING */
assert(!state_is_closing(peer->state));
stage.fulfill.fulfill = HTLC_FULFILL; prog.stage.fulfill.fulfill = HTLC_FULFILL;
stage.fulfill.r = *r; prog.stage.fulfill.r = *r;
stage.fulfill.id = id; prog.stage.fulfill.id = id;
set_htlc_command(peer, jsoncmd, CMD_SEND_HTLC_FULFILL, &stage); /* FIXME: get rid of htlc_progress, just use htlc_staging. */
idata.htlc_prog = &prog;
state_event(peer, CMD_SEND_HTLC_FULFILL, &idata);
} }
if (jsoncmd)
command_success(jsoncmd, null_response(jsoncmd));
return true;
} }
static void peer_breakdown(struct peer *peer) static void peer_breakdown(struct peer *peer)
{ {
peer->cond = PEER_CLOSED;
/* If we have a closing tx, use it. */ /* If we have a closing tx, use it. */
if (peer->closing.their_sig) { if (peer->closing.their_sig) {
log_unusual(peer->log, "Peer breakdown: sending close tx"); log_unusual(peer->log, "Peer breakdown: sending close tx");
@ -279,7 +282,6 @@ static bool committed_to_htlcs(const struct peer *peer)
static struct io_plan *peer_close(struct io_conn *conn, struct peer *peer) static struct io_plan *peer_close(struct io_conn *conn, struct peer *peer)
{ {
/* Tell writer to wrap it up (may have to xmit first) */ /* Tell writer to wrap it up (may have to xmit first) */
peer->cond = PEER_CLOSED;
io_wake(peer); io_wake(peer);
/* We do nothing more. */ /* We do nothing more. */
return io_wait(conn, NULL, io_never, NULL); return io_wait(conn, NULL, io_never, NULL);
@ -419,9 +421,6 @@ static struct io_plan *closing_pkt_in(struct io_conn *conn, struct peer *peer)
return peer_read_packet(conn, peer, closing_pkt_in); return peer_read_packet(conn, peer, closing_pkt_in);
} }
/* FIXME: Predecls are bad, move up! */
static void try_command(struct peer *peer);
/* This is the io loop while we're clearing. */ /* This is the io loop while we're clearing. */
static struct io_plan *clearing_pkt_in(struct io_conn *conn, struct peer *peer) static struct io_plan *clearing_pkt_in(struct io_conn *conn, struct peer *peer)
{ {
@ -438,11 +437,9 @@ static struct io_plan *clearing_pkt_in(struct io_conn *conn, struct peer *peer)
err = accept_pkt_revocation(peer, pkt); err = accept_pkt_revocation(peer, pkt);
if (!err) { if (!err) {
set_peer_state(peer, STATE_CLEARING, __func__); set_peer_state(peer, STATE_CLEARING, __func__);
peer_cmd_complete(peer, CMD_SUCCESS); peer_update_complete(peer, NULL);
peer->cond = PEER_CMD_OK; } else
/* In case command is queued. */ peer_update_complete(peer, "bad revocation");
try_command(peer);
}
} }
break; break;
@ -540,24 +537,12 @@ static void state_single(struct peer *peer,
const enum state_input input, const enum state_input input,
const union input *idata) const union input *idata)
{ {
enum command_status status;
const struct bitcoin_tx *broadcast; const struct bitcoin_tx *broadcast;
enum state newstate;
size_t old_outpkts = tal_count(peer->outpkt); size_t old_outpkts = tal_count(peer->outpkt);
status = state(peer, input, idata, &broadcast); newstate = state(peer, input, idata, &broadcast);
log_debug(peer->log, "Peer condition: %s", cstatus_name(peer->cond)); set_peer_state(peer, newstate, input_name(input));
switch (status) {
case CMD_NONE:
break;
case CMD_SUCCESS:
log_add(peer->log, " (command success)");
peer_cmd_complete(peer, CMD_SUCCESS);
break;
case CMD_FAIL:
log_add(peer->log, " (command FAIL)");
peer_cmd_complete(peer, CMD_FAIL);
break;
}
/* If we added uncommitted changes, we should have set them to send. */ /* If we added uncommitted changes, we should have set them to send. */
if (peer_uncommitted_changes(peer)) if (peer_uncommitted_changes(peer))
@ -570,22 +555,18 @@ static void state_single(struct peer *peer,
if (broadcast) if (broadcast)
broadcast_tx(peer, broadcast); broadcast_tx(peer, broadcast);
if (peer->state == STATE_ERR_BREAKDOWN) if (peer->state == STATE_CLEARING
|| peer->state == STATE_CLEARING_COMMITTING) {
peer_start_clearing(peer);
} else if (state_is_error(peer->state)) {
/* Breakdown is common, others less so. */
if (peer->state != STATE_ERR_BREAKDOWN)
log_broken(peer->log, "Entered error state %s",
state_name(peer->state));
peer_breakdown(peer); peer_breakdown(peer);
/* Start output if not running already; it will close conn. */ /* Start output if not running already; it will close conn. */
if (peer->cond == PEER_CLOSED)
io_wake(peer); io_wake(peer);
else if (peer->state == STATE_CLEARING
|| peer->state == STATE_CLEARING_COMMITTING)
peer_start_clearing(peer);
/* FIXME: Some of these should just result in this peer being killed? */
else if (state_is_error(peer->state)) {
log_broken(peer->log, "Entered error state %s",
state_name(peer->state));
fatal("Peer entered error state");
} }
/* Break out and free this peer if it's completely done. */ /* Break out and free this peer if it's completely done. */
@ -593,52 +574,6 @@ static void state_single(struct peer *peer,
io_break(peer); io_break(peer);
} }
static void try_command(struct peer *peer)
{
/* If we can accept a command, and we have one queued, run it. */
while (peer->cond == PEER_CMD_OK
&& !list_empty(&peer->pending_cmd)) {
struct pending_cmd *pend = list_pop(&peer->pending_cmd,
struct pending_cmd, list);
assert(peer->curr_cmd.cmd == INPUT_NONE);
/* This can fail to enqueue a command! */
pend->dequeue(peer, pend->arg);
tal_free(pend);
if (peer->curr_cmd.cmd != INPUT_NONE) {
state_single(peer, peer->curr_cmd.cmd,
&peer->curr_cmd.cmddata);
}
}
}
#define queue_cmd(peer, cb, arg) \
queue_cmd_((peer), \
typesafe_cb_preargs(void, void *, \
(cb), (arg), \
struct peer *), \
(arg))
static bool queue_cmd_(struct peer *peer,
void (*dequeue)(struct peer *peer, void *arg),
void *arg)
{
struct pending_cmd *pend;
if (peer->cond == PEER_CLOSED)
return false;
pend = tal(peer, struct pending_cmd);
pend->dequeue = dequeue;
pend->arg = arg;
list_add_tail(&peer->pending_cmd, &pend->list);
try_command(peer);
return true;
};
static void UNNEEDED queue_input(struct peer *peer, static void UNNEEDED queue_input(struct peer *peer,
enum state_input input, enum state_input input,
const union input *idata) const union input *idata)
@ -657,7 +592,7 @@ static void state_event(struct peer *peer,
{ {
struct pending_input *pend; struct pending_input *pend;
if (state_is_closing(peer->state)) { if (!state_is_opening(peer->state) && !state_is_normal(peer->state)) {
log_unusual(peer->log, log_unusual(peer->log,
"Unexpected input %s while state %s", "Unexpected input %s while state %s",
input_name(input), state_name(peer->state)); input_name(input), state_name(peer->state));
@ -670,8 +605,6 @@ static void state_event(struct peer *peer,
state_event(peer, pend->input, &pend->idata); state_event(peer, pend->input, &pend->idata);
tal_free(pend); tal_free(pend);
} }
try_command(peer);
} }
static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer) static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer)
@ -684,7 +617,7 @@ static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer)
if (n == 0) { if (n == 0) {
/* We close the connection once we've sent everything. */ /* We close the connection once we've sent everything. */
if (peer->cond == PEER_CLOSED) if (!state_can_io(peer->state))
return io_close(conn); return io_close(conn);
return io_out_wait(conn, peer, pkt_out, peer); return io_out_wait(conn, peer, pkt_out, peer);
} }
@ -711,7 +644,7 @@ static struct io_plan *pkt_in(struct io_conn *conn, struct peer *peer)
idata.pkt = tal_steal(ctx, peer->inpkt); idata.pkt = tal_steal(ctx, peer->inpkt);
/* We ignore packets if they tell us to. */ /* We ignore packets if they tell us to. */
if (!peer->fake_close && peer->cond != PEER_CLOSED) { if (!peer->fake_close && state_can_io(peer->state)) {
state_event(peer, peer->inpkt->pkt_case, &idata); state_event(peer, peer->inpkt->pkt_case, &idata);
} }
@ -721,11 +654,6 @@ static struct io_plan *pkt_in(struct io_conn *conn, struct peer *peer)
return peer_read_packet(conn, peer, pkt_in); return peer_read_packet(conn, peer, pkt_in);
} }
static void do_anchor_offer(struct peer *peer, void *unused)
{
set_current_command(peer, peer->local.offer_anchor, NULL, NULL);
}
/* Crypto is on, we are live. */ /* Crypto is on, we are live. */
static struct io_plan *peer_crypto_on(struct io_conn *conn, struct peer *peer) static struct io_plan *peer_crypto_on(struct io_conn *conn, struct peer *peer)
{ {
@ -735,8 +663,7 @@ static struct io_plan *peer_crypto_on(struct io_conn *conn, struct peer *peer)
assert(peer->state == STATE_INIT); assert(peer->state == STATE_INIT);
/* Using queue_cmd is overkill here, but it works. */ state_event(peer, peer->local.offer_anchor, NULL);
queue_cmd(peer, do_anchor_offer, NULL);
return io_duplex(conn, return io_duplex(conn,
peer_read_packet(conn, peer, pkt_in), peer_read_packet(conn, peer, pkt_in),
@ -768,13 +695,10 @@ static void peer_disconnect(struct io_conn *conn, struct peer *peer)
io_break(peer); io_break(peer);
return; return;
} }
/* FIXME: Try to reconnect. */
/* This is an expected close. */
if (peer->cond == PEER_CLOSED)
return;
if (peer->state != STATE_ERR_BREAKDOWN) { /* This is an unexpected close. */
if (!state_is_onchain(peer->state) && !state_is_error(peer->state)) {
/* FIXME: Try to reconnect. */
set_peer_state(peer, STATE_ERR_BREAKDOWN, "peer_disconnect"); set_peer_state(peer, STATE_ERR_BREAKDOWN, "peer_disconnect");
peer_breakdown(peer); peer_breakdown(peer);
} }
@ -783,11 +707,7 @@ static void peer_disconnect(struct io_conn *conn, struct peer *peer)
static void do_commit(struct peer *peer, struct command *jsoncmd) static void do_commit(struct peer *peer, struct command *jsoncmd)
{ {
/* We can have changes we suggested, or changes they suggested. */ /* We can have changes we suggested, or changes they suggested. */
if (peer->remote.staging_cstate if (peer_uncommitted_changes(peer)) {
&& peer->remote.commit
&& peer->remote.commit->cstate
&& peer->remote.staging_cstate->changes
!= peer->remote.commit->cstate->changes) {
log_debug(peer->log, "do_commit: sending commit command"); log_debug(peer->log, "do_commit: sending commit command");
command_send_commit(peer, jsoncmd); command_send_commit(peer, jsoncmd);
return; return;
@ -803,7 +723,17 @@ static void do_commit(struct peer *peer, struct command *jsoncmd)
static void try_commit(struct peer *peer) static void try_commit(struct peer *peer)
{ {
peer->commit_timer = NULL; peer->commit_timer = NULL;
queue_cmd(peer, do_commit, (struct command *)NULL);
if (state_can_commit(peer->state))
do_commit(peer, NULL);
else {
/* FIXME: try again when we receive revocation, rather
* than using timer! */
log_debug(peer->log, "try_commit: state=%s, re-queueing timer",
state_name(peer->state));
remote_changes_pending(peer);
}
} }
void remote_changes_pending(struct peer *peer) void remote_changes_pending(struct peer *peer)
@ -834,7 +764,6 @@ static struct peer *new_peer(struct lightningd_state *dstate,
list_add(&dstate->peers, &peer->list); list_add(&dstate->peers, &peer->list);
peer->state = STATE_INIT; peer->state = STATE_INIT;
peer->cond = PEER_CMD_OK;
peer->dstate = dstate; peer->dstate = dstate;
peer->addr.type = addr_type; peer->addr.type = addr_type;
peer->addr.protocol = addr_protocol; peer->addr.protocol = addr_protocol;
@ -842,7 +771,7 @@ static struct peer *new_peer(struct lightningd_state *dstate,
peer->secrets = NULL; peer->secrets = NULL;
list_head_init(&peer->watches); list_head_init(&peer->watches);
peer->outpkt = tal_arr(peer, Pkt *, 0); peer->outpkt = tal_arr(peer, Pkt *, 0);
peer->curr_cmd.cmd = INPUT_NONE; peer->commit_jsoncmd = NULL;
list_head_init(&peer->pending_cmd); list_head_init(&peer->pending_cmd);
list_head_init(&peer->pending_input); list_head_init(&peer->pending_input);
list_head_init(&peer->outgoing_txs); list_head_init(&peer->outgoing_txs);
@ -1832,7 +1761,10 @@ static void check_for_resolution(struct peer *peer,
* *irrevocably resolved*. * *irrevocably resolved*.
*/ */
set_peer_state(peer, STATE_CLOSED, "check_for_resolution"); set_peer_state(peer, STATE_CLOSED, "check_for_resolution");
io_break(peer);
/* It's theoretically possible that peer is still writing output */
if (!peer->conn)
io_break(peer);
} }
/* 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
@ -1843,6 +1775,8 @@ static void anchor_spent(struct peer *peer,
void *unused) void *unused)
{ {
struct sha256_double txid; struct sha256_double txid;
Pkt *err;
enum state newstate;
assert(input_num < tx->input_count); assert(input_num < tx->input_count);
@ -1850,22 +1784,6 @@ static void anchor_spent(struct peer *peer,
if (input_num != 0) if (input_num != 0)
fatal("Anchor spend by non-single input tx"); fatal("Anchor spend by non-single input tx");
/* BOLT #onchain:
*
* A node SHOULD fail the connection if it is not already
* closed when it sees the funding transaction spent.
*/
if (peer->cond != PEER_CLOSED) {
peer->cond = PEER_CLOSED;
/* BOLT #onchain:
*
* A node MAY send a descriptive error packet in this case.
*/
queue_pkt_err(peer,
pkt_err(peer, "Funding transaction spent!"));
}
/* We may have been following a different spend. Forget it. */ /* We may have been following a different spend. Forget it. */
reset_onchain_closing(peer); reset_onchain_closing(peer);
@ -1875,25 +1793,23 @@ static void anchor_spent(struct peer *peer,
peer->closing_onchain.ci = find_commit(peer->remote.commit, &txid); peer->closing_onchain.ci = find_commit(peer->remote.commit, &txid);
if (peer->closing_onchain.ci) { if (peer->closing_onchain.ci) {
if (peer->closing_onchain.ci->revocation_preimage) { if (peer->closing_onchain.ci->revocation_preimage) {
set_peer_state(peer, STATE_CLOSE_ONCHAIN_CHEATED, newstate = STATE_CLOSE_ONCHAIN_CHEATED;
"anchor_spent"); err = pkt_err(peer, "Revoked transaction seen");
resolve_cheating(peer); resolve_cheating(peer);
} else { } else {
set_peer_state(peer, newstate = STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL;
STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL, err = pkt_err(peer, "Unilateral close tx seen");
"anchor_spent");
resolve_their_unilateral(peer); resolve_their_unilateral(peer);
} }
} else if (txidmatch(peer->local.commit->tx, &txid)) { } else if (txidmatch(peer->local.commit->tx, &txid)) {
set_peer_state(peer, newstate = STATE_CLOSE_ONCHAIN_OUR_UNILATERAL;
STATE_CLOSE_ONCHAIN_OUR_UNILATERAL, /* We're almost certainly closed to them by now. */
"anchor_spent"); err = pkt_err(peer, "Our own unilateral close tx seen");
peer->closing_onchain.ci = peer->local.commit; peer->closing_onchain.ci = peer->local.commit;
resolve_our_unilateral(peer); resolve_our_unilateral(peer);
} else if (is_mutual_close(peer, tx)) { } else if (is_mutual_close(peer, tx)) {
set_peer_state(peer, newstate = STATE_CLOSE_ONCHAIN_MUTUAL;
STATE_CLOSE_ONCHAIN_MUTUAL, err = NULL;
"anchor_spent");
resolve_mutual_close(peer); resolve_mutual_close(peer);
} else { } else {
/* BOLT #onchain: /* BOLT #onchain:
@ -1914,6 +1830,26 @@ static void anchor_spent(struct peer *peer,
peer->anchor.watches->depthok = INPUT_NONE; peer->anchor.watches->depthok = INPUT_NONE;
return; return;
} }
/* BOLT #onchain:
*
* A node MAY send a descriptive error packet in this case.
*/
if (err && state_can_io(peer->state))
queue_pkt_err(peer, err);
set_peer_state(peer, newstate, "anchor_spent");
/* If we've just closed connection, make output close it. */
io_wake(peer);
/* BOLT #onchain:
*
* A node SHOULD fail the connection if it is not already
* closed when it sees the funding transaction spent.
*/
assert(!state_can_io(peer->state));
assert(peer->closing_onchain.resolved != NULL); assert(peer->closing_onchain.resolved != NULL);
watch_tx(tx, peer, tx, check_for_resolution, NULL); watch_tx(tx, peer, tx, check_for_resolution, NULL);
@ -2355,7 +2291,6 @@ static void json_getpeers(struct command *cmd,
json_object_start(response, NULL); json_object_start(response, NULL);
json_add_string(response, "name", log_prefix(p->log)); json_add_string(response, "name", log_prefix(p->log));
json_add_string(response, "state", state_name(p->state)); json_add_string(response, "state", state_name(p->state));
json_add_string(response, "cmd", input_name(p->curr_cmd.cmd));
/* This is only valid after crypto setup. */ /* This is only valid after crypto setup. */
if (p->state != STATE_INIT) if (p->state != STATE_INIT)
@ -2407,10 +2342,12 @@ const struct json_command getpeers_command = {
/* FIXME: Make sure no HTLCs in any unrevoked commit tx are live. */ /* FIXME: Make sure no HTLCs in any unrevoked commit tx are live. */
static void check_htlc_expiry(struct peer *peer, void *unused) static void check_htlc_expiry(struct peer *peer)
{ {
size_t i; size_t i;
log_debug(peer->log, "Expiry timedout!");
/* Check their currently still-existing htlcs for expiry: /* Check their currently still-existing htlcs for expiry:
* We eliminate them from staging as we go. */ * We eliminate them from staging as we go. */
for (i = 0; i < tal_count(peer->remote.staging_cstate->side[THEIRS].htlcs); i++) { for (i = 0; i < tal_count(peer->remote.staging_cstate->side[THEIRS].htlcs); i++) {
@ -2425,17 +2362,12 @@ static void check_htlc_expiry(struct peer *peer, void *unused)
< abs_locktime_to_seconds(&htlc->expiry)) < abs_locktime_to_seconds(&htlc->expiry))
continue; continue;
/* This can fail only if we're in an error state. */
command_htlc_fail(peer, htlc->id, NULL); command_htlc_fail(peer, htlc->id, NULL);
return; return;
} }
} }
static void htlc_expiry_timeout(struct peer *peer)
{
log_debug(peer->log, "Expiry timedout!");
queue_cmd(peer, check_htlc_expiry, NULL);
}
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)
{ {
@ -2445,26 +2377,24 @@ void peer_add_htlc_expiry(struct peer *peer,
absexpiry.ts.tv_sec = abs_locktime_to_seconds(expiry) + 30; absexpiry.ts.tv_sec = abs_locktime_to_seconds(expiry) + 30;
absexpiry.ts.tv_nsec = 0; absexpiry.ts.tv_nsec = 0;
new_abstimer(peer->dstate, peer, absexpiry, htlc_expiry_timeout, peer); new_abstimer(peer->dstate, peer, absexpiry, check_htlc_expiry, peer);
} }
struct newhtlc {
struct channel_htlc htlc;
struct command *jsoncmd;
};
/* We do final checks just before we start command, as things may have /* We do final checks just before we start command, as things may have
* changed. */ * changed. */
static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc) static void do_newhtlc(struct peer *peer,
const struct channel_htlc *htlc,
struct command *jsoncmd)
{ {
struct channel_state *cstate; struct channel_state *cstate;
union htlc_staging stage; union input idata;
struct htlc_progress prog;
/* Now we can assign counter and guarantee uniqueness. */ /* Now we can assign counter and guarantee uniqueness. */
newhtlc->htlc.id = peer->htlc_id_counter; prog.stage.add.add = HTLC_ADD;
stage.add.add = HTLC_ADD; prog.stage.add.htlc = *htlc;
stage.add.htlc = newhtlc->htlc; prog.stage.add.htlc.id = peer->htlc_id_counter;
/* BOLT #2: /* BOLT #2:
* *
* A node MUST NOT add a HTLC if it would result in it * A node MUST NOT add a HTLC if it would result in it
@ -2472,16 +2402,12 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
*/ */
if (tal_count(peer->local.staging_cstate->side[OURS].htlcs) == 300 if (tal_count(peer->local.staging_cstate->side[OURS].htlcs) == 300
|| tal_count(peer->remote.staging_cstate->side[OURS].htlcs) == 300) { || tal_count(peer->remote.staging_cstate->side[OURS].htlcs) == 300) {
command_fail(newhtlc->jsoncmd, "Too many HTLCs"); command_fail(jsoncmd, "Too many HTLCs");
return; return;
} }
/* BOLT #2: if (!state_can_add_htlc(peer->state)) {
* command_fail(jsoncmd, "Channel closing, state %s",
* A node MUST NOT send a `update_add_htlc` after a `close_clearing`
*/
if (state_is_closing(peer->state)) {
command_fail(newhtlc->jsoncmd, "Channel closing, state %s",
state_name(peer->state)); state_name(peer->state));
return; return;
} }
@ -2491,25 +2417,25 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
* A node MUST NOT offer `amount_msat` it cannot pay for in * A node MUST NOT offer `amount_msat` it cannot pay for in
* both commitment transactions at the current `fee_rate` * both commitment transactions at the current `fee_rate`
*/ */
cstate = copy_funding(newhtlc, peer->remote.staging_cstate); cstate = copy_funding(jsoncmd, peer->remote.staging_cstate);
if (!funding_add_htlc(cstate, newhtlc->htlc.msatoshis, if (!funding_add_htlc(cstate, htlc->msatoshis,
&newhtlc->htlc.expiry, &newhtlc->htlc.rhash, &htlc->expiry, &htlc->rhash,
newhtlc->htlc.id, OURS)) { htlc->id, OURS)) {
command_fail(newhtlc->jsoncmd, command_fail(jsoncmd,
"Cannot afford %"PRIu64 "Cannot afford %"PRIu64
" milli-satoshis in their commit tx", " milli-satoshis in their commit tx",
newhtlc->htlc.msatoshis); htlc->msatoshis);
return; return;
} }
cstate = copy_funding(newhtlc, peer->local.staging_cstate); cstate = copy_funding(jsoncmd, peer->local.staging_cstate);
if (!funding_add_htlc(cstate, newhtlc->htlc.msatoshis, if (!funding_add_htlc(cstate, htlc->msatoshis,
&newhtlc->htlc.expiry, &newhtlc->htlc.rhash, &htlc->expiry, &htlc->rhash,
newhtlc->htlc.id, OURS)) { htlc->id, OURS)) {
command_fail(newhtlc->jsoncmd, command_fail(jsoncmd,
"Cannot afford %"PRIu64 "Cannot afford %"PRIu64
" milli-satoshis in our commit tx", " milli-satoshis in our commit tx",
newhtlc->htlc.msatoshis); htlc->msatoshis);
return; return;
} }
@ -2517,7 +2443,9 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
peer->htlc_id_counter++; peer->htlc_id_counter++;
/* FIXME: Never propose duplicate rvalues? */ /* FIXME: Never propose duplicate rvalues? */
set_htlc_command(peer, newhtlc->jsoncmd, CMD_SEND_HTLC_ADD, &stage); idata.htlc_prog = &prog;
state_event(peer, CMD_SEND_HTLC_ADD, &idata);
command_success(jsoncmd, null_response(jsoncmd));
} }
static void json_newhtlc(struct command *cmd, static void json_newhtlc(struct command *cmd,
@ -2526,7 +2454,7 @@ static void json_newhtlc(struct command *cmd,
struct peer *peer; struct peer *peer;
jsmntok_t *peeridtok, *msatoshistok, *expirytok, *rhashtok; jsmntok_t *peeridtok, *msatoshistok, *expirytok, *rhashtok;
unsigned int expiry; unsigned int expiry;
struct newhtlc *newhtlc; struct channel_htlc htlc;
if (!json_get_params(buffer, params, if (!json_get_params(buffer, params,
"peerid", &peeridtok, "peerid", &peeridtok,
@ -2549,11 +2477,7 @@ static void json_newhtlc(struct command *cmd,
return; return;
} }
/* Attach to cmd until it's complete. */ if (!json_tok_u64(buffer, msatoshistok, &htlc.msatoshis)) {
newhtlc = tal(cmd, struct newhtlc);
newhtlc->jsoncmd = cmd;
if (!json_tok_u64(buffer, msatoshistok, &newhtlc->htlc.msatoshis)) {
command_fail(cmd, "'%.*s' is not a valid number", command_fail(cmd, "'%.*s' is not a valid number",
(int)(msatoshistok->end - msatoshistok->start), (int)(msatoshistok->end - msatoshistok->start),
buffer + msatoshistok->start); buffer + msatoshistok->start);
@ -2566,20 +2490,20 @@ static void json_newhtlc(struct command *cmd,
return; return;
} }
if (!seconds_to_abs_locktime(expiry, &newhtlc->htlc.expiry)) { if (!seconds_to_abs_locktime(expiry, &htlc.expiry)) {
command_fail(cmd, "'%.*s' is not a valid number", command_fail(cmd, "'%.*s' is not a valid number",
(int)(expirytok->end - expirytok->start), (int)(expirytok->end - expirytok->start),
buffer + expirytok->start); buffer + expirytok->start);
return; return;
} }
if (abs_locktime_to_seconds(&newhtlc->htlc.expiry) < if (abs_locktime_to_seconds(&htlc.expiry) <
controlled_time().ts.tv_sec + peer->dstate->config.min_expiry) { controlled_time().ts.tv_sec + peer->dstate->config.min_expiry) {
command_fail(cmd, "HTLC expiry too soon!"); command_fail(cmd, "HTLC expiry too soon!");
return; return;
} }
if (abs_locktime_to_seconds(&newhtlc->htlc.expiry) > if (abs_locktime_to_seconds(&htlc.expiry) >
controlled_time().ts.tv_sec + peer->dstate->config.max_expiry) { controlled_time().ts.tv_sec + peer->dstate->config.max_expiry) {
command_fail(cmd, "HTLC expiry too far!"); command_fail(cmd, "HTLC expiry too far!");
return; return;
@ -2587,16 +2511,15 @@ static void json_newhtlc(struct command *cmd,
if (!hex_decode(buffer + rhashtok->start, if (!hex_decode(buffer + rhashtok->start,
rhashtok->end - rhashtok->start, rhashtok->end - rhashtok->start,
&newhtlc->htlc.rhash, &htlc.rhash,
sizeof(newhtlc->htlc.rhash))) { sizeof(htlc.rhash))) {
command_fail(cmd, "'%.*s' is not a valid sha256 hash", command_fail(cmd, "'%.*s' is not a valid sha256 hash",
(int)(rhashtok->end - rhashtok->start), (int)(rhashtok->end - rhashtok->start),
buffer + rhashtok->start); buffer + rhashtok->start);
return; return;
} }
if (!queue_cmd(peer, do_newhtlc, newhtlc)) do_newhtlc(peer, &htlc, cmd);
command_fail(cmd, "Peer closing");
} }
/* FIXME: Use HTLC ids, not r values! */ /* FIXME: Use HTLC ids, not r values! */
@ -2618,27 +2541,24 @@ static size_t find_their_committed_htlc(struct peer *peer,
return funding_find_htlc(peer->remote.staging_cstate, rhash, THEIRS); return funding_find_htlc(peer->remote.staging_cstate, rhash, THEIRS);
} }
struct fulfillhtlc {
struct command *jsoncmd;
struct sha256 r;
};
static void do_fullfill(struct peer *peer, static void do_fullfill(struct peer *peer,
struct fulfillhtlc *fulfillhtlc) const struct sha256 *r,
struct command *jsoncmd)
{ {
struct sha256 rhash; struct sha256 rhash;
size_t i; size_t i;
u64 id; u64 id;
sha256(&rhash, &fulfillhtlc->r, sizeof(fulfillhtlc->r)); sha256(&rhash, r, sizeof(*r));
i = find_their_committed_htlc(peer, &rhash); i = find_their_committed_htlc(peer, &rhash);
if (i == -1) { if (i == -1) {
command_fail(fulfillhtlc->jsoncmd, "preimage htlc not found"); command_fail(jsoncmd, "preimage htlc not found");
return; return;
} }
id = peer->remote.staging_cstate->side[THEIRS].htlcs[i].id; id = peer->remote.staging_cstate->side[THEIRS].htlcs[i].id;
command_htlc_fulfill(peer, id, &fulfillhtlc->r, fulfillhtlc->jsoncmd); command_htlc_fulfill(peer, id, r, jsoncmd);
} }
static void json_fulfillhtlc(struct command *cmd, static void json_fulfillhtlc(struct command *cmd,
@ -2646,7 +2566,7 @@ static void json_fulfillhtlc(struct command *cmd,
{ {
struct peer *peer; struct peer *peer;
jsmntok_t *peeridtok, *rtok; jsmntok_t *peeridtok, *rtok;
struct fulfillhtlc *fulfillhtlc; struct sha256 r;
if (!json_get_params(buffer, params, if (!json_get_params(buffer, params,
"peerid", &peeridtok, "peerid", &peeridtok,
@ -2667,20 +2587,15 @@ static void json_fulfillhtlc(struct command *cmd,
return; return;
} }
fulfillhtlc = tal(cmd, struct fulfillhtlc);
fulfillhtlc->jsoncmd = cmd;
if (!hex_decode(buffer + rtok->start, if (!hex_decode(buffer + rtok->start,
rtok->end - rtok->start, rtok->end - rtok->start,
&fulfillhtlc->r, sizeof(fulfillhtlc->r))) { &r, sizeof(r))) {
command_fail(cmd, "'%.*s' is not a valid sha256 preimage", command_fail(cmd, "'%.*s' is not a valid sha256 preimage",
(int)(rtok->end - rtok->start), (int)(rtok->end - rtok->start),
buffer + rtok->start); buffer + rtok->start);
return; return;
} }
do_fullfill(peer, &r, cmd);
if (!queue_cmd(peer, do_fullfill, fulfillhtlc))
command_fail(cmd, "Peer closing");
} }
const struct json_command fulfillhtlc_command = { const struct json_command fulfillhtlc_command = {
@ -2690,26 +2605,23 @@ const struct json_command fulfillhtlc_command = {
"Returns an empty result on success" "Returns an empty result on success"
}; };
struct failhtlc { /* FIXME: Flatten into json_failhtlc. */
struct command *jsoncmd;
struct sha256 rhash;
};
static void do_failhtlc(struct peer *peer, static void do_failhtlc(struct peer *peer,
struct failhtlc *failhtlc) const struct sha256 *rhash,
struct command *jsoncmd)
{ {
size_t i; size_t i;
u64 id; u64 id;
/* Look in peer->remote.staging_cstate->a, as that's where we'll /* Look in peer->remote.staging_cstate->a, as that's where we'll
* immediately remove it from: avoids double-handling. */ * immediately remove it from: avoids double-handling. */
i = find_their_committed_htlc(peer, &failhtlc->rhash); i = find_their_committed_htlc(peer, rhash);
if (i == -1) { if (i == -1) {
command_fail(failhtlc->jsoncmd, "htlc not found"); command_fail(jsoncmd, "htlc not found");
return; return;
} }
id = peer->remote.staging_cstate->side[THEIRS].htlcs[i].id; id = peer->remote.staging_cstate->side[THEIRS].htlcs[i].id;
command_htlc_fail(peer, id, failhtlc->jsoncmd); command_htlc_fail(peer, id, jsoncmd);
} }
static void json_failhtlc(struct command *cmd, static void json_failhtlc(struct command *cmd,
@ -2717,7 +2629,7 @@ static void json_failhtlc(struct command *cmd,
{ {
struct peer *peer; struct peer *peer;
jsmntok_t *peeridtok, *rhashtok; jsmntok_t *peeridtok, *rhashtok;
struct failhtlc *failhtlc; struct sha256 rhash;
if (!json_get_params(buffer, params, if (!json_get_params(buffer, params,
"peerid", &peeridtok, "peerid", &peeridtok,
@ -2738,20 +2650,16 @@ static void json_failhtlc(struct command *cmd,
return; return;
} }
failhtlc = tal(cmd, struct failhtlc);
failhtlc->jsoncmd = cmd;
if (!hex_decode(buffer + rhashtok->start, if (!hex_decode(buffer + rhashtok->start,
rhashtok->end - rhashtok->start, rhashtok->end - rhashtok->start,
&failhtlc->rhash, sizeof(failhtlc->rhash))) { &rhash, sizeof(rhash))) {
command_fail(cmd, "'%.*s' is not a valid sha256 preimage", command_fail(cmd, "'%.*s' is not a valid sha256 preimage",
(int)(rhashtok->end - rhashtok->start), (int)(rhashtok->end - rhashtok->start),
buffer + rhashtok->start); buffer + rhashtok->start);
return; return;
} }
if (!queue_cmd(peer, do_failhtlc, failhtlc)) do_failhtlc(peer, &rhash, cmd);
command_fail(cmd, "Peer closing");
} }
const struct json_command failhtlc_command = { const struct json_command failhtlc_command = {
@ -2785,8 +2693,12 @@ static void json_commit(struct command *cmd,
return; return;
} }
if (!queue_cmd(peer, do_commit, cmd)) if (!state_can_commit(peer->state)) {
command_fail(cmd, "Peer closing"); command_fail(cmd, "peer in state %s", state_name(peer->state));
return;
}
do_commit(peer, cmd);
} }
const struct json_command commit_command = { const struct json_command commit_command = {
@ -2815,16 +2727,12 @@ static void json_close(struct command *cmd,
return; return;
} }
if (state_is_closing(peer->state)) { if (!state_is_normal(peer->state) && !state_is_opening(peer->state)) {
command_fail(cmd, "Peer is already closing: state %s", command_fail(cmd, "Peer is already closing: state %s",
state_name(peer->state)); state_name(peer->state));
return; return;
} }
/* Closing causes any current command to fail. */
if (peer->curr_cmd.cmd != INPUT_NONE)
peer_cmd_complete(peer, CMD_FAIL);
if (peer->state == STATE_NORMAL_COMMITTING) if (peer->state == STATE_NORMAL_COMMITTING)
set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__); set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__);
else else

19
daemon/peer.h

@ -118,18 +118,11 @@ struct peer {
/* State in state machine. */ /* State in state machine. */
enum state state; enum state state;
/* Condition of communications */
enum state_peercond cond;
/* Network connection. */ /* Network connection. */
struct io_conn *conn; struct io_conn *conn;
/* Current command (or INPUT_NONE) */ /* If we're doing a commit, this is the command which triggered it */
struct { struct command *commit_jsoncmd;
enum state_input cmd;
union input cmddata;
struct command *jsoncmd;
} curr_cmd;
/* Pending inputs. */ /* Pending inputs. */
struct list_head pending_input; struct list_head pending_input;
@ -234,8 +227,6 @@ void setup_listeners(struct lightningd_state *dstate, unsigned int portnum);
/* Populates very first peer->{local,remote}.commit->{tx,cstate} */ /* Populates very first peer->{local,remote}.commit->{tx,cstate} */
bool setup_first_commit(struct peer *peer); bool setup_first_commit(struct peer *peer);
void set_peer_state(struct peer *peer, enum state newstate, const char *why);
/* Set up timer: we have something we can commit. */ /* Set up timer: we have something we can commit. */
void remote_changes_pending(struct peer *peer); void remote_changes_pending(struct peer *peer);
@ -243,6 +234,12 @@ void remote_changes_pending(struct peer *peer);
void add_unacked(struct peer_visible_state *which, void add_unacked(struct peer_visible_state *which,
const union htlc_staging *stage); const union htlc_staging *stage);
/* Peer has recieved revocation, or problem (if non-NULL). */
void peer_update_complete(struct peer *peer, const char *problem);
/* Peer has completed open, or problem (if non-NULL). */
void peer_open_complete(struct peer *peer, const char *problem);
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);

13
daemon/test/test.sh

@ -298,18 +298,16 @@ if [ -n "$TIMEOUT_ANCHOR" ]; then
MOCKTIME=$((`date +%s` + 7200 + 3 * 1200 + 1)) MOCKTIME=$((`date +%s` + 7200 + 3 * 1200 + 1))
lcli1 dev-mocktime $MOCKTIME lcli1 dev-mocktime $MOCKTIME
lcli2 dev-mocktime $MOCKTIME
# This will crash immediately # Node2 should have gone via STATE_ERR_ANCHOR_TIMEOUT, then closed.
if $LCLI2 dev-mocktime $MOCKTIME 2> /dev/null; then lcli2 getlog | grep STATE_ERR_ANCHOR_TIMEOUT
echo Node2 did not crash >&2 check_no_peers lcli2
exit 1
fi
$FGREP 'Entered error state STATE_ERR_ANCHOR_TIMEOUT' $DIR2/crash.log
# Node1 should be disconnected. # Node1 should be disconnected.
check_peerconnected lcli1 false check_peerconnected lcli1 false
# It should send out commit tx; mine it. # Node1 should send out commit tx; mine it.
check_tx_spend lcli1 check_tx_spend lcli1
$CLI generate 1 $CLI generate 1
@ -335,6 +333,7 @@ if [ -n "$TIMEOUT_ANCHOR" ]; then
check_no_peers lcli1 check_no_peers lcli1
lcli1 stop lcli1 stop
lcli2 stop
all_ok all_ok
fi fi

20
names.c

@ -23,26 +23,6 @@ const char *input_name(enum state_input in)
return "unknown"; return "unknown";
} }
const char *cstatus_name(enum command_status cstatus)
{
size_t i;
for (i = 0; enum_command_status_names[i].name; i++)
if (enum_command_status_names[i].v == cstatus)
return enum_command_status_names[i].name;
return "unknown";
}
const char *peercond_name(enum state_peercond peercond)
{
size_t i;
for (i = 0; enum_state_peercond_names[i].name; i++)
if (enum_state_peercond_names[i].v == peercond)
return enum_state_peercond_names[i].name;
return "unknown";
}
const char *pkt_name(Pkt__PktCase pkt) const char *pkt_name(Pkt__PktCase pkt)
{ {
size_t i; size_t i;

2
names.h

@ -6,7 +6,5 @@
const char *state_name(enum state s); const char *state_name(enum state s);
const char *input_name(enum state_input in); const char *input_name(enum state_input in);
const char *cstatus_name(enum command_status cstatus);
const char *peercond_name(enum state_peercond peercond);
const char *pkt_name(Pkt__PktCase pkt); const char *pkt_name(Pkt__PktCase pkt);
#endif /* LIGHTNING_NAMES_H */ #endif /* LIGHTNING_NAMES_H */

207
state.c

@ -6,57 +6,25 @@
#include <names.h> #include <names.h>
#include <state.h> #include <state.h>
static enum command_status next_state(struct peer *peer, static enum state next_state(struct peer *peer,
const enum state_input input, const enum state_input input,
enum command_status cstatus, const enum state state)
const enum state state)
{ {
assert(peer->state != state); assert(peer->state != state);
set_peer_state(peer, state, input_name(input)); return state;
return cstatus;
} }
/* /*
* Simple marker to note we don't update state. * Simple marker to note we don't update state.
* *
* This happens in three cases: * This happens in normal state except when committing or closing.
* - We're ignoring packets while closing.
* - We stop watching an on-chain HTLC: we indicate that we want
* INPUT_NO_MORE_HTLCS when we get the last one.
* - HTLC add/remove in STATE_NORMAL.
*/ */
static enum command_status unchanged_state(const struct peer *peer, static enum state unchanged_state(const struct peer *peer,
const enum state_input input, const enum state_input input)
enum command_status cstatus)
{ {
log_debug(peer->log, "%s: %s unchanged", log_debug(peer->log, "%s: %s unchanged",
input_name(input), state_name(peer->state)); input_name(input), state_name(peer->state));
return cstatus; return peer->state;
}
static void change_peer_cond(struct peer *peer,
enum state_peercond old,
enum state_peercond new)
{
assert(peer->cond == old);
peer->cond = new;
}
static void complete_cmd(struct peer *peer, enum command_status *statusp,
enum command_status status)
{
change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK);
*statusp = status;
}
/* FIXME: We do this when a command succeeds instantly, and
* state is unchanged. */
static enum command_status instant_cmd_success(struct peer *peer,
enum command_status cstatus)
{
assert(peer->cond == PEER_CMD_OK);
assert(cstatus == CMD_NONE);
return CMD_SUCCESS;
} }
static void queue_tx_broadcast(const struct bitcoin_tx **broadcast, static void queue_tx_broadcast(const struct bitcoin_tx **broadcast,
@ -67,13 +35,12 @@ static void queue_tx_broadcast(const struct bitcoin_tx **broadcast,
*broadcast = tx; *broadcast = tx;
} }
enum command_status state(struct peer *peer, enum state state(struct peer *peer,
const enum state_input input, const enum state_input input,
const union input *idata, const union input *idata,
const struct bitcoin_tx **broadcast) const struct bitcoin_tx **broadcast)
{ {
Pkt *err; Pkt *err;
enum command_status cstatus = CMD_NONE;
*broadcast = NULL; *broadcast = NULL;
@ -85,14 +52,12 @@ enum command_status state(struct peer *peer,
if (input_is(input, CMD_OPEN_WITH_ANCHOR)) { if (input_is(input, CMD_OPEN_WITH_ANCHOR)) {
queue_pkt_open(peer, queue_pkt_open(peer,
OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR); OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR);
change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); return next_state(peer, input,
return next_state(peer, input, cstatus,
STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR); STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR);
} else if (input_is(input, CMD_OPEN_WITHOUT_ANCHOR)) { } else if (input_is(input, CMD_OPEN_WITHOUT_ANCHOR)) {
change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY);
queue_pkt_open(peer, queue_pkt_open(peer,
OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR); OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR);
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR); STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR);
} }
break; break;
@ -100,12 +65,12 @@ enum command_status state(struct peer *peer,
if (input_is(input, PKT_OPEN)) { if (input_is(input, PKT_OPEN)) {
err = accept_pkt_open(peer, idata->pkt); err = accept_pkt_open(peer, idata->pkt);
if (err) { if (err) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, err->error->problem);
goto err_breakdown; goto err_breakdown;
} }
return next_state(peer, input, cstatus, STATE_OPEN_WAIT_FOR_ANCHOR); return next_state(peer, input, STATE_OPEN_WAIT_FOR_ANCHOR);
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
@ -113,25 +78,25 @@ enum command_status state(struct peer *peer,
if (input_is(input, PKT_OPEN)) { if (input_is(input, PKT_OPEN)) {
err = accept_pkt_open(peer, idata->pkt); err = accept_pkt_open(peer, idata->pkt);
if (err) { if (err) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, err->error->problem);
goto err_breakdown; goto err_breakdown;
} }
bitcoin_create_anchor(peer, BITCOIN_ANCHOR_CREATED); bitcoin_create_anchor(peer, BITCOIN_ANCHOR_CREATED);
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAIT_FOR_ANCHOR_CREATE); STATE_OPEN_WAIT_FOR_ANCHOR_CREATE);
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
case STATE_OPEN_WAIT_FOR_ANCHOR_CREATE: case STATE_OPEN_WAIT_FOR_ANCHOR_CREATE:
if (input_is(input, BITCOIN_ANCHOR_CREATED)) { if (input_is(input, BITCOIN_ANCHOR_CREATED)) {
queue_pkt_anchor(peer); queue_pkt_anchor(peer);
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAIT_FOR_COMMIT_SIG); STATE_OPEN_WAIT_FOR_COMMIT_SIG);
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
bitcoin_release_anchor(peer, BITCOIN_ANCHOR_CREATED); bitcoin_release_anchor(peer, BITCOIN_ANCHOR_CREATED);
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
@ -139,7 +104,7 @@ enum command_status state(struct peer *peer,
if (input_is(input, PKT_OPEN_ANCHOR)) { if (input_is(input, PKT_OPEN_ANCHOR)) {
err = accept_pkt_anchor(peer, idata->pkt); err = accept_pkt_anchor(peer, idata->pkt);
if (err) { if (err) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, err->error->problem);
goto err_breakdown; goto err_breakdown;
} }
queue_pkt_open_commit_sig(peer); queue_pkt_open_commit_sig(peer);
@ -147,10 +112,10 @@ enum command_status state(struct peer *peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT); BITCOIN_ANCHOR_TIMEOUT);
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAITING_THEIRANCHOR); STATE_OPEN_WAITING_THEIRANCHOR);
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
@ -159,18 +124,18 @@ enum command_status state(struct peer *peer,
err = accept_pkt_open_commit_sig(peer, idata->pkt); err = accept_pkt_open_commit_sig(peer, idata->pkt);
if (err) { if (err) {
bitcoin_release_anchor(peer, INPUT_NONE); bitcoin_release_anchor(peer, INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, err->error->problem);
goto err_breakdown; goto err_breakdown;
} }
queue_tx_broadcast(broadcast, bitcoin_anchor(peer)); queue_tx_broadcast(broadcast, bitcoin_anchor(peer));
peer_watch_anchor(peer, peer_watch_anchor(peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE); INPUT_NONE);
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAITING_OURANCHOR); STATE_OPEN_WAITING_OURANCHOR);
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
bitcoin_release_anchor(peer, INPUT_NONE); bitcoin_release_anchor(peer, INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
@ -178,14 +143,14 @@ enum command_status state(struct peer *peer,
if (input_is(input, PKT_OPEN_COMPLETE)) { if (input_is(input, PKT_OPEN_COMPLETE)) {
err = accept_pkt_open_complete(peer, idata->pkt); err = accept_pkt_open_complete(peer, idata->pkt);
if (err) { if (err) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, err->error->problem);
/* We no longer care about anchor depth. */ /* We no longer care about anchor depth. */
peer_unwatch_anchor_depth(peer, peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE); INPUT_NONE);
goto err_breakdown; goto err_breakdown;
} }
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED); STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED);
} }
/* Fall thru */ /* Fall thru */
@ -193,24 +158,24 @@ enum command_status state(struct peer *peer,
if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) { if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) {
queue_pkt_open_complete(peer); queue_pkt_open_complete(peer);
if (peer->state == STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED) { if (peer->state == STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED) {
complete_cmd(peer, &cstatus, CMD_SUCCESS); peer_open_complete(peer, NULL);
return next_state(peer, input, cstatus, STATE_NORMAL); return next_state(peer, input, STATE_NORMAL);
} }
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR); STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR);
} else if (input_is(input, PKT_CLOSE_CLEARING)) { } else if (input_is(input, PKT_CLOSE_CLEARING)) {
/* We no longer care about anchor depth. */ /* We no longer care about anchor depth. */
peer_unwatch_anchor_depth(peer, peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE); INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "Received PKT_CLOSE_CLEARING");
goto accept_clearing; goto accept_clearing;
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
/* We no longer care about anchor depth. */ /* We no longer care about anchor depth. */
peer_unwatch_anchor_depth(peer, peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE); INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
@ -218,14 +183,14 @@ enum command_status state(struct peer *peer,
if (input_is(input, PKT_OPEN_COMPLETE)) { if (input_is(input, PKT_OPEN_COMPLETE)) {
err = accept_pkt_open_complete(peer, idata->pkt); err = accept_pkt_open_complete(peer, idata->pkt);
if (err) { if (err) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, err->error->problem);
/* We no longer care about anchor depth. */ /* We no longer care about anchor depth. */
peer_unwatch_anchor_depth(peer, peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT); BITCOIN_ANCHOR_TIMEOUT);
goto err_breakdown; goto err_breakdown;
} }
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED); STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED);
} }
/* Fall thru */ /* Fall thru */
@ -233,47 +198,42 @@ enum command_status state(struct peer *peer,
if (input_is(input, BITCOIN_ANCHOR_TIMEOUT)) { if (input_is(input, BITCOIN_ANCHOR_TIMEOUT)) {
/* Anchor didn't reach blockchain in reasonable time. */ /* Anchor didn't reach blockchain in reasonable time. */
queue_pkt_err(peer, pkt_err(peer, "Anchor timed out")); queue_pkt_err(peer, pkt_err(peer, "Anchor timed out"));
return next_state(peer, input, cstatus, STATE_ERR_ANCHOR_TIMEOUT); return next_state(peer, input, STATE_ERR_ANCHOR_TIMEOUT);
} else if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) { } else if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) {
queue_pkt_open_complete(peer); queue_pkt_open_complete(peer);
if (peer->state == STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED) { if (peer->state == STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED) {
complete_cmd(peer, &cstatus, CMD_SUCCESS); peer_open_complete(peer, NULL);
return next_state(peer, input, cstatus, STATE_NORMAL); return next_state(peer, input, STATE_NORMAL);
} }
return next_state(peer, input, cstatus, return next_state(peer, input,
STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR); STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR);
} else if (input_is(input, PKT_CLOSE_CLEARING)) { } else if (input_is(input, PKT_CLOSE_CLEARING)) {
/* We no longer care about anchor depth. */ /* We no longer care about anchor depth. */
peer_unwatch_anchor_depth(peer, peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT); BITCOIN_ANCHOR_TIMEOUT);
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "Received PKT_CLOSE_CLEARING");
goto accept_clearing; goto accept_clearing;
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
/* We no longer care about anchor depth. */ /* We no longer care about anchor depth. */
peer_unwatch_anchor_depth(peer, peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT); BITCOIN_ANCHOR_TIMEOUT);
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
case STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR: case STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR:
case STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR: case STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR:
if (input_is(input, PKT_OPEN_COMPLETE)) { if (input_is(input, PKT_OPEN_COMPLETE)) {
/* Ready for business! Anchorer goes first. */ /* Ready for business! */
if (peer->state == STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR) { peer_open_complete(peer, NULL);
complete_cmd(peer, &cstatus, CMD_SUCCESS); return next_state(peer, input, STATE_NORMAL);
return next_state(peer, input, cstatus, STATE_NORMAL);
} else {
complete_cmd(peer, &cstatus, CMD_SUCCESS);
return next_state(peer, input, cstatus, STATE_NORMAL);
}
} else if (input_is(input, PKT_CLOSE_CLEARING)) { } else if (input_is(input, PKT_CLOSE_CLEARING)) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "Received PKT_CLOSE_CLEARING");
goto accept_clearing; goto accept_clearing;
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
complete_cmd(peer, &cstatus, CMD_FAIL); peer_open_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
@ -282,71 +242,66 @@ enum command_status state(struct peer *peer,
* Channel normal operating states. * Channel normal operating states.
*/ */
case STATE_NORMAL: case STATE_NORMAL:
/*
* FIXME: For simplicity, we disallow new staging requests
* while a commit is outstanding.
*/
/* You can only issue this command one at a time. */ /* You can only issue this command one at a time. */
if (input_is(input, CMD_SEND_COMMIT)) { if (input_is(input, CMD_SEND_COMMIT)) {
queue_pkt_commit(peer); queue_pkt_commit(peer);
change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); return next_state(peer, input, STATE_NORMAL_COMMITTING);
return next_state(peer, input, cstatus, STATE_NORMAL_COMMITTING); }
} else if (input_is(input, CMD_SEND_HTLC_ADD)) { /* Fall through... */
case STATE_NORMAL_COMMITTING:
if (input_is(input, CMD_SEND_HTLC_ADD)) {
/* We are to send an HTLC add. */ /* We are to send an HTLC add. */
queue_pkt_htlc_add(peer, idata->htlc_prog); queue_pkt_htlc_add(peer, idata->htlc_prog);
return instant_cmd_success(peer, cstatus); return unchanged_state(peer, input);
} else if (input_is(input, CMD_SEND_HTLC_FULFILL)) { } else if (input_is(input, CMD_SEND_HTLC_FULFILL)) {
assert(idata->htlc_prog->stage.type == HTLC_FULFILL); assert(idata->htlc_prog->stage.type == HTLC_FULFILL);
/* We are to send an HTLC fulfill. */ /* We are to send an HTLC fulfill. */
queue_pkt_htlc_fulfill(peer, queue_pkt_htlc_fulfill(peer,
idata->htlc_prog->stage.fulfill.id, idata->htlc_prog->stage.fulfill.id,
&idata->htlc_prog->stage.fulfill.r); &idata->htlc_prog->stage.fulfill.r);
return instant_cmd_success(peer, cstatus); return unchanged_state(peer, input);
} else if (input_is(input, CMD_SEND_HTLC_FAIL)) { } else if (input_is(input, CMD_SEND_HTLC_FAIL)) {
assert(idata->htlc_prog->stage.type == HTLC_FAIL); assert(idata->htlc_prog->stage.type == HTLC_FAIL);
/* We are to send an HTLC fail. */ /* We are to send an HTLC fail. */
queue_pkt_htlc_fail(peer, idata->htlc_prog->stage.fail.id); queue_pkt_htlc_fail(peer, idata->htlc_prog->stage.fail.id);
return instant_cmd_success(peer, cstatus); return unchanged_state(peer, input);
} }
/* Fall through... */
case STATE_NORMAL_COMMITTING:
/* Only expect revocation in STATE_NORMAL_COMMITTING */ /* Only expect revocation in STATE_NORMAL_COMMITTING */
if (peer->state == STATE_NORMAL_COMMITTING else if (peer->state == STATE_NORMAL_COMMITTING
&& input_is(input, PKT_UPDATE_REVOCATION)) { && input_is(input, PKT_UPDATE_REVOCATION)) {
err = accept_pkt_revocation(peer, idata->pkt); err = accept_pkt_revocation(peer, idata->pkt);
if (err) { if (err)
complete_cmd(peer, &cstatus, CMD_FAIL); goto err_breakdown_maybe_committing;
goto err_breakdown; peer_update_complete(peer, NULL);
} return next_state(peer, input, STATE_NORMAL);
complete_cmd(peer, &cstatus, CMD_SUCCESS);
return next_state(peer, input, cstatus, STATE_NORMAL);
} }
if (input_is(input, PKT_UPDATE_ADD_HTLC)) { if (input_is(input, PKT_UPDATE_ADD_HTLC)) {
err = accept_pkt_htlc_add(peer, idata->pkt); err = accept_pkt_htlc_add(peer, idata->pkt);
if (err) if (err)
goto err_breakdown; goto err_breakdown_maybe_committing;
return unchanged_state(peer, input, cstatus); return unchanged_state(peer, input);
} else if (input_is(input, PKT_UPDATE_FULFILL_HTLC)) { } else if (input_is(input, PKT_UPDATE_FULFILL_HTLC)) {
err = accept_pkt_htlc_fulfill(peer, idata->pkt); err = accept_pkt_htlc_fulfill(peer, idata->pkt);
if (err) if (err)
goto err_breakdown; goto err_breakdown_maybe_committing;
return unchanged_state(peer, input, cstatus); return unchanged_state(peer, input);
} else if (input_is(input, PKT_UPDATE_FAIL_HTLC)) { } else if (input_is(input, PKT_UPDATE_FAIL_HTLC)) {
err = accept_pkt_htlc_fail(peer, idata->pkt); err = accept_pkt_htlc_fail(peer, idata->pkt);
if (err) if (err)
goto err_breakdown; goto err_breakdown_maybe_committing;
return unchanged_state(peer, input, cstatus); return unchanged_state(peer, input);
} else if (input_is(input, PKT_UPDATE_COMMIT)) { } else if (input_is(input, PKT_UPDATE_COMMIT)) {
err = accept_pkt_commit(peer, idata->pkt); err = accept_pkt_commit(peer, idata->pkt);
if (err) if (err)
goto err_breakdown; goto err_breakdown_maybe_committing;
queue_pkt_revocation(peer); queue_pkt_revocation(peer);
return unchanged_state(peer, input, cstatus); return unchanged_state(peer, input);
} else if (input_is(input, PKT_CLOSE_CLEARING)) { } else if (input_is(input, PKT_CLOSE_CLEARING)) {
goto accept_clearing; goto accept_clearing;
} else if (input_is_pkt(input)) { } else if (input_is_pkt(input)) {
if (peer->state == STATE_NORMAL_COMMITTING)
peer_update_complete(peer, "unexpected packet");
goto unexpected_pkt; goto unexpected_pkt;
} }
break; break;
@ -365,11 +320,11 @@ enum command_status state(struct peer *peer,
case STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL: case STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL:
case STATE_CLOSE_ONCHAIN_OUR_UNILATERAL: case STATE_CLOSE_ONCHAIN_OUR_UNILATERAL:
case STATE_CLOSE_ONCHAIN_MUTUAL: case STATE_CLOSE_ONCHAIN_MUTUAL:
return next_state(peer, input, cstatus, STATE_ERR_INTERNAL); return next_state(peer, input, STATE_ERR_INTERNAL);
} }
/* State machine should handle all possible states. */ /* State machine should handle all possible states. */
return next_state(peer, input, cstatus, STATE_ERR_INTERNAL); return next_state(peer, input, STATE_ERR_INTERNAL);
unexpected_pkt: unexpected_pkt:
peer_unexpected_pkt(peer, idata->pkt); peer_unexpected_pkt(peer, idata->pkt);
@ -379,19 +334,23 @@ unexpected_pkt:
goto breakdown; goto breakdown;
} }
err = pkt_err_unexpected(peer, idata->pkt); err = pkt_err_unexpected(peer, idata->pkt);
goto err_breakdown;
err_breakdown_maybe_committing:
if (peer->state == STATE_NORMAL_COMMITTING)
peer_update_complete(peer, "bad packet");
err_breakdown: err_breakdown:
queue_pkt_err(peer, err); queue_pkt_err(peer, err);
breakdown: breakdown:
return next_state(peer, input, cstatus, STATE_ERR_BREAKDOWN); return next_state(peer, input, STATE_ERR_BREAKDOWN);
accept_clearing: accept_clearing:
err = accept_pkt_close_clearing(peer, idata->pkt); err = accept_pkt_close_clearing(peer, idata->pkt);
if (err) if (err)
goto err_breakdown; goto err_breakdown_maybe_committing;
/* If we've sent commit, we're still waiting for it when clearing. */ /* If we've sent commit, we're still waiting for it when clearing. */
if (peer->state == STATE_NORMAL_COMMITTING) if (peer->state == STATE_NORMAL_COMMITTING)
return next_state(peer, input, cstatus, STATE_CLEARING_COMMITTING); return next_state(peer, input, STATE_CLEARING_COMMITTING);
return next_state(peer, input, cstatus, STATE_CLEARING); return next_state(peer, input, STATE_CLEARING);
} }

61
state.h

@ -16,14 +16,61 @@
static inline bool state_is_error(enum state s) static inline bool state_is_error(enum state s)
{ {
return s >= STATE_ERR_ANCHOR_TIMEOUT && s <= STATE_ERR_INTERNAL; return s >= STATE_ERR_BREAKDOWN && s <= STATE_ERR_INTERNAL;
} }
static inline bool state_is_closing(enum state s) static inline bool state_is_clearing(enum state s)
{ {
return s >= STATE_CLEARING; return s == STATE_CLEARING || s == STATE_CLEARING_COMMITTING;
} }
static inline bool state_is_onchain(enum state s)
{
return s >= STATE_CLOSE_ONCHAIN_CHEATED
&& s <= STATE_CLOSE_ONCHAIN_MUTUAL;
}
static inline bool state_is_normal(enum state s)
{
return s == STATE_NORMAL || s == STATE_NORMAL_COMMITTING;
}
static inline bool state_is_opening(enum state s)
{
return s < STATE_NORMAL;
}
static inline bool state_can_io(enum state s)
{
if (state_is_error(s))
return false;
if (s == STATE_CLOSED)
return false;
if (state_is_onchain(s))
return false;
return true;
}
static inline bool state_can_commit(enum state s)
{
return s == STATE_NORMAL || s == STATE_CLEARING;
}
/* BOLT #2:
*
* A node MUST NOT send a `update_add_htlc` after a `close_clearing`
*/
static inline bool state_can_add_htlc(enum state s)
{
return state_is_normal(s);
}
static inline bool state_can_remove_htlc(enum state s)
{
return state_is_normal(s) || state_is_clearing(s);
}
struct peer; struct peer;
struct bitcoin_tx; struct bitcoin_tx;
@ -48,10 +95,10 @@ union input {
} *htlc_onchain; } *htlc_onchain;
}; };
enum command_status state(struct peer *peer, enum state state(struct peer *peer,
const enum state_input input, const enum state_input input,
const union input *idata, const union input *idata,
const struct bitcoin_tx **broadcast); const struct bitcoin_tx **broadcast);
/* Any CMD_SEND_HTLC_* */ /* Any CMD_SEND_HTLC_* */
#define CMD_SEND_UPDATE_ANY INPUT_MAX #define CMD_SEND_UPDATE_ANY INPUT_MAX

19
state_types.h

@ -117,23 +117,4 @@ enum state_input {
INPUT_MAX INPUT_MAX
}; };
enum state_peercond {
/* Ready to accept a new command. */
PEER_CMD_OK,
/* Don't send me commands, I'm busy. */
PEER_BUSY,
/* No more packets, I'm closed. */
PEER_CLOSED
};
enum command_status {
/* Nothing changed. */
CMD_NONE,
/* Command succeeded. */
CMD_SUCCESS,
/* Failed. */
CMD_FAIL
};
#endif /* LIGHTNING_STATE_TYPES_H */ #endif /* LIGHTNING_STATE_TYPES_H */

Loading…
Cancel
Save