diff --git a/daemon/peer.c b/daemon/peer.c index 25e3f7573..fe1970758 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -90,132 +90,135 @@ static struct json_result *null_response(const tal_t *ctx) return response; } -static void complete_json_command(struct command *jsoncmd, - enum command_status status) +/* FIXME: reorder to avoid predecls. */ +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 (status == CMD_FAIL) - /* FIXME: y'know, details. */ - command_fail(jsoncmd, "Failed"); - else { - assert(status == CMD_SUCCESS); - command_success(jsoncmd, null_response(jsoncmd)); - } + if (!problem) { + log_debug(peer->log, "peer_update_complete"); + if (peer->commit_jsoncmd) + command_success(peer->commit_jsoncmd, + null_response(peer->commit_jsoncmd)); + } else { + log_unusual(peer->log, "peer_update_complete failed: %s", + problem); + if (peer->commit_jsoncmd) + command_fail(peer->commit_jsoncmd, "%s", problem); + } + + /* Simply unset it: it will free itself. */ + peer->commit_jsoncmd = NULL; + + /* Have we got more changes in the meantime? */ + if (peer_uncommitted_changes(peer)) { + log_debug(peer->log, "peer_update_complete: more changes!"); + remote_changes_pending(peer); } } -static void peer_cmd_complete(struct peer *peer, enum command_status status) +void peer_open_complete(struct peer *peer, const char *problem) { - complete_json_command(peer->curr_cmd.jsoncmd, status); - peer->curr_cmd.jsoncmd = NULL; - peer->curr_cmd.cmd = INPUT_NONE; + if (problem) + log_unusual(peer->log, "peer open failed: %s", problem); + else + log_debug(peer->log, "peer open complete"); } -static void set_current_command(struct peer *peer, - const enum state_input input, - void *idata, - struct command *jsoncmd) -{ - assert(peer->curr_cmd.cmd == INPUT_NONE); - assert(input != INPUT_NONE); - - peer->curr_cmd.cmd = input; - /* 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, state_name(peer->state), state_name(newstate)); peer->state = newstate; } +/* FIXME: Flatten into do_commit. */ static void command_send_commit(struct peer *peer, struct command *jsoncmd) { - assert(peer->curr_cmd.cmd == INPUT_NONE); - - /* Commands should still be blocked during this! */ - assert(peer->state != STATE_CLEARING_COMMITTING); - + assert(state_can_commit(peer->state)); + assert(!peer->commit_jsoncmd); + + peer->commit_jsoncmd = jsoncmd; if (peer->state == STATE_CLEARING) { - peer->cond = PEER_BUSY; - peer->curr_cmd.jsoncmd = jsoncmd; queue_pkt_commit(peer); set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__); } else { /* You can send commands if closing; peer->cond == CLOSING */ - assert(!state_is_closing(peer->state)); - set_current_command(peer, CMD_SEND_COMMIT, NULL, jsoncmd); + assert(peer->state == STATE_NORMAL); + state_event(peer, CMD_SEND_COMMIT, NULL); } } -static void set_htlc_command(struct peer *peer, - struct command *jsoncmd, - enum state_input cmd, - const union htlc_staging *stage) +static bool command_htlc_fail(struct peer *peer, u64 id, struct command *jsoncmd) { - /* FIXME: memleak! */ - /* FIXME: Get rid of struct htlc_progress */ - struct htlc_progress *progress = tal(peer, struct htlc_progress); - - progress->stage = *stage; - set_current_command(peer, cmd, progress, jsoncmd); -} - -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 (!state_can_remove_htlc(peer->state)) { + if (jsoncmd) + command_fail(jsoncmd, + "htlc_fail not possible in state %s", + state_name(peer->state)); + return false; + } if (peer->state == STATE_CLEARING) { queue_pkt_htlc_fail(peer, id); - complete_json_command(jsoncmd, CMD_SUCCESS); } 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.fail.fail = HTLC_FAIL; - stage.fail.id = id; - set_htlc_command(peer, jsoncmd, CMD_SEND_HTLC_FAIL, &stage); + prog.stage.fail.fail = HTLC_FAIL; + prog.stage.fail.id = id; + /* FIXME: get rid of htlc_progress, just use htlc_staging. */ + idata.htlc_prog = &prog; + state_event(peer, CMD_SEND_HTLC_FAIL, &idata); } + + 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, const struct sha256 *r, 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! */ assert(peer->state != STATE_CLEARING_COMMITTING); if (peer->state == STATE_CLEARING) { queue_pkt_htlc_fulfill(peer, id, r); - complete_json_command(jsoncmd, CMD_SUCCESS); } 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; - stage.fulfill.r = *r; - stage.fulfill.id = id; - set_htlc_command(peer, jsoncmd, CMD_SEND_HTLC_FULFILL, &stage); + prog.stage.fulfill.fulfill = HTLC_FULFILL; + prog.stage.fulfill.r = *r; + prog.stage.fulfill.id = id; + /* 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) { - peer->cond = PEER_CLOSED; - /* If we have a closing tx, use it. */ if (peer->closing.their_sig) { 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) { /* Tell writer to wrap it up (may have to xmit first) */ - peer->cond = PEER_CLOSED; io_wake(peer); /* We do nothing more. */ 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); } -/* FIXME: Predecls are bad, move up! */ -static void try_command(struct peer *peer); - /* This is the io loop while we're clearing. */ 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); if (!err) { set_peer_state(peer, STATE_CLEARING, __func__); - peer_cmd_complete(peer, CMD_SUCCESS); - peer->cond = PEER_CMD_OK; - /* In case command is queued. */ - try_command(peer); - } + peer_update_complete(peer, NULL); + } else + peer_update_complete(peer, "bad revocation"); } break; @@ -540,24 +537,12 @@ static void state_single(struct peer *peer, const enum state_input input, const union input *idata) { - enum command_status status; const struct bitcoin_tx *broadcast; + enum state newstate; size_t old_outpkts = tal_count(peer->outpkt); - status = state(peer, input, idata, &broadcast); - log_debug(peer->log, "Peer condition: %s", cstatus_name(peer->cond)); - 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; - } + newstate = state(peer, input, idata, &broadcast); + set_peer_state(peer, newstate, input_name(input)); /* If we added uncommitted changes, we should have set them to send. */ if (peer_uncommitted_changes(peer)) @@ -570,22 +555,18 @@ static void state_single(struct peer *peer, if (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); - /* Start output if not running already; it will close conn. */ - if (peer->cond == PEER_CLOSED) + /* Start output if not running already; it will close conn. */ 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. */ @@ -593,52 +574,6 @@ static void state_single(struct peer *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, enum state_input input, const union input *idata) @@ -657,7 +592,7 @@ static void state_event(struct peer *peer, { 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, "Unexpected input %s while state %s", 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); tal_free(pend); } - - try_command(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) { /* 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_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); /* 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); } @@ -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); } -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. */ 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); - /* Using queue_cmd is overkill here, but it works. */ - queue_cmd(peer, do_anchor_offer, NULL); + state_event(peer, peer->local.offer_anchor, NULL); return io_duplex(conn, 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); 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"); 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) { /* We can have changes we suggested, or changes they suggested. */ - if (peer->remote.staging_cstate - && peer->remote.commit - && peer->remote.commit->cstate - && peer->remote.staging_cstate->changes - != peer->remote.commit->cstate->changes) { + if (peer_uncommitted_changes(peer)) { log_debug(peer->log, "do_commit: sending commit command"); command_send_commit(peer, jsoncmd); return; @@ -803,7 +723,17 @@ static void do_commit(struct peer *peer, struct command *jsoncmd) static void try_commit(struct peer *peer) { 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) @@ -834,7 +764,6 @@ static struct peer *new_peer(struct lightningd_state *dstate, list_add(&dstate->peers, &peer->list); peer->state = STATE_INIT; - peer->cond = PEER_CMD_OK; peer->dstate = dstate; peer->addr.type = addr_type; peer->addr.protocol = addr_protocol; @@ -842,7 +771,7 @@ static struct peer *new_peer(struct lightningd_state *dstate, peer->secrets = NULL; list_head_init(&peer->watches); 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_input); list_head_init(&peer->outgoing_txs); @@ -1832,7 +1761,10 @@ static void check_for_resolution(struct peer *peer, * *irrevocably resolved*. */ 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 @@ -1843,6 +1775,8 @@ static void anchor_spent(struct peer *peer, void *unused) { struct sha256_double txid; + Pkt *err; + enum state newstate; assert(input_num < tx->input_count); @@ -1850,22 +1784,6 @@ static void anchor_spent(struct peer *peer, if (input_num != 0) 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. */ 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); if (peer->closing_onchain.ci) { if (peer->closing_onchain.ci->revocation_preimage) { - set_peer_state(peer, STATE_CLOSE_ONCHAIN_CHEATED, - "anchor_spent"); + newstate = STATE_CLOSE_ONCHAIN_CHEATED; + err = pkt_err(peer, "Revoked transaction seen"); resolve_cheating(peer); } else { - set_peer_state(peer, - STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL, - "anchor_spent"); + newstate = STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL; + err = pkt_err(peer, "Unilateral close tx seen"); resolve_their_unilateral(peer); } } else if (txidmatch(peer->local.commit->tx, &txid)) { - set_peer_state(peer, - STATE_CLOSE_ONCHAIN_OUR_UNILATERAL, - "anchor_spent"); + newstate = STATE_CLOSE_ONCHAIN_OUR_UNILATERAL; + /* We're almost certainly closed to them by now. */ + err = pkt_err(peer, "Our own unilateral close tx seen"); peer->closing_onchain.ci = peer->local.commit; resolve_our_unilateral(peer); } else if (is_mutual_close(peer, tx)) { - set_peer_state(peer, - STATE_CLOSE_ONCHAIN_MUTUAL, - "anchor_spent"); + newstate = STATE_CLOSE_ONCHAIN_MUTUAL; + err = NULL; resolve_mutual_close(peer); } else { /* BOLT #onchain: @@ -1914,6 +1830,26 @@ static void anchor_spent(struct peer *peer, peer->anchor.watches->depthok = INPUT_NONE; 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); 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_add_string(response, "name", log_prefix(p->log)); 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. */ 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. */ -static void check_htlc_expiry(struct peer *peer, void *unused) +static void check_htlc_expiry(struct peer *peer) { size_t i; + log_debug(peer->log, "Expiry timedout!"); + /* Check their currently still-existing htlcs for expiry: * We eliminate them from staging as we go. */ 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)) continue; + /* This can fail only if we're in an error state. */ command_htlc_fail(peer, htlc->id, NULL); 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, 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_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 * 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; - union htlc_staging stage; + union input idata; + struct htlc_progress prog; /* Now we can assign counter and guarantee uniqueness. */ - newhtlc->htlc.id = peer->htlc_id_counter; - stage.add.add = HTLC_ADD; - stage.add.htlc = newhtlc->htlc; - + prog.stage.add.add = HTLC_ADD; + prog.stage.add.htlc = *htlc; + prog.stage.add.htlc.id = peer->htlc_id_counter; + /* BOLT #2: * * 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 || tal_count(peer->remote.staging_cstate->side[OURS].htlcs) == 300) { - command_fail(newhtlc->jsoncmd, "Too many HTLCs"); + command_fail(jsoncmd, "Too many HTLCs"); return; } - /* BOLT #2: - * - * 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", + if (!state_can_add_htlc(peer->state)) { + command_fail(jsoncmd, "Channel closing, state %s", state_name(peer->state)); 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 * both commitment transactions at the current `fee_rate` */ - cstate = copy_funding(newhtlc, peer->remote.staging_cstate); - if (!funding_add_htlc(cstate, newhtlc->htlc.msatoshis, - &newhtlc->htlc.expiry, &newhtlc->htlc.rhash, - newhtlc->htlc.id, OURS)) { - command_fail(newhtlc->jsoncmd, + cstate = copy_funding(jsoncmd, peer->remote.staging_cstate); + if (!funding_add_htlc(cstate, htlc->msatoshis, + &htlc->expiry, &htlc->rhash, + htlc->id, OURS)) { + command_fail(jsoncmd, "Cannot afford %"PRIu64 " milli-satoshis in their commit tx", - newhtlc->htlc.msatoshis); + htlc->msatoshis); return; } - cstate = copy_funding(newhtlc, peer->local.staging_cstate); - if (!funding_add_htlc(cstate, newhtlc->htlc.msatoshis, - &newhtlc->htlc.expiry, &newhtlc->htlc.rhash, - newhtlc->htlc.id, OURS)) { - command_fail(newhtlc->jsoncmd, + cstate = copy_funding(jsoncmd, peer->local.staging_cstate); + if (!funding_add_htlc(cstate, htlc->msatoshis, + &htlc->expiry, &htlc->rhash, + htlc->id, OURS)) { + command_fail(jsoncmd, "Cannot afford %"PRIu64 " milli-satoshis in our commit tx", - newhtlc->htlc.msatoshis); + htlc->msatoshis); return; } @@ -2517,7 +2443,9 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc) peer->htlc_id_counter++; /* 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, @@ -2526,7 +2454,7 @@ static void json_newhtlc(struct command *cmd, struct peer *peer; jsmntok_t *peeridtok, *msatoshistok, *expirytok, *rhashtok; unsigned int expiry; - struct newhtlc *newhtlc; + struct channel_htlc htlc; if (!json_get_params(buffer, params, "peerid", &peeridtok, @@ -2549,11 +2477,7 @@ static void json_newhtlc(struct command *cmd, return; } - /* Attach to cmd until it's complete. */ - newhtlc = tal(cmd, struct newhtlc); - newhtlc->jsoncmd = cmd; - - if (!json_tok_u64(buffer, msatoshistok, &newhtlc->htlc.msatoshis)) { + if (!json_tok_u64(buffer, msatoshistok, &htlc.msatoshis)) { command_fail(cmd, "'%.*s' is not a valid number", (int)(msatoshistok->end - msatoshistok->start), buffer + msatoshistok->start); @@ -2566,20 +2490,20 @@ static void json_newhtlc(struct command *cmd, 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", (int)(expirytok->end - expirytok->start), buffer + expirytok->start); 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) { command_fail(cmd, "HTLC expiry too soon!"); 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) { command_fail(cmd, "HTLC expiry too far!"); return; @@ -2587,16 +2511,15 @@ static void json_newhtlc(struct command *cmd, if (!hex_decode(buffer + rhashtok->start, rhashtok->end - rhashtok->start, - &newhtlc->htlc.rhash, - sizeof(newhtlc->htlc.rhash))) { + &htlc.rhash, + sizeof(htlc.rhash))) { command_fail(cmd, "'%.*s' is not a valid sha256 hash", (int)(rhashtok->end - rhashtok->start), buffer + rhashtok->start); return; } - if (!queue_cmd(peer, do_newhtlc, newhtlc)) - command_fail(cmd, "Peer closing"); + do_newhtlc(peer, &htlc, cmd); } /* 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); } -struct fulfillhtlc { - struct command *jsoncmd; - struct sha256 r; -}; - static void do_fullfill(struct peer *peer, - struct fulfillhtlc *fulfillhtlc) + const struct sha256 *r, + struct command *jsoncmd) { struct sha256 rhash; size_t i; u64 id; - sha256(&rhash, &fulfillhtlc->r, sizeof(fulfillhtlc->r)); + sha256(&rhash, r, sizeof(*r)); i = find_their_committed_htlc(peer, &rhash); if (i == -1) { - command_fail(fulfillhtlc->jsoncmd, "preimage htlc not found"); + command_fail(jsoncmd, "preimage htlc not found"); return; } + 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, @@ -2646,7 +2566,7 @@ static void json_fulfillhtlc(struct command *cmd, { struct peer *peer; jsmntok_t *peeridtok, *rtok; - struct fulfillhtlc *fulfillhtlc; + struct sha256 r; if (!json_get_params(buffer, params, "peerid", &peeridtok, @@ -2667,20 +2587,15 @@ static void json_fulfillhtlc(struct command *cmd, return; } - fulfillhtlc = tal(cmd, struct fulfillhtlc); - fulfillhtlc->jsoncmd = cmd; - if (!hex_decode(buffer + rtok->start, rtok->end - rtok->start, - &fulfillhtlc->r, sizeof(fulfillhtlc->r))) { + &r, sizeof(r))) { command_fail(cmd, "'%.*s' is not a valid sha256 preimage", (int)(rtok->end - rtok->start), buffer + rtok->start); return; } - - if (!queue_cmd(peer, do_fullfill, fulfillhtlc)) - command_fail(cmd, "Peer closing"); + do_fullfill(peer, &r, cmd); } const struct json_command fulfillhtlc_command = { @@ -2690,26 +2605,23 @@ const struct json_command fulfillhtlc_command = { "Returns an empty result on success" }; -struct failhtlc { - struct command *jsoncmd; - struct sha256 rhash; -}; - +/* FIXME: Flatten into json_failhtlc. */ static void do_failhtlc(struct peer *peer, - struct failhtlc *failhtlc) + const struct sha256 *rhash, + struct command *jsoncmd) { size_t i; u64 id; /* Look in peer->remote.staging_cstate->a, as that's where we'll * 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) { - command_fail(failhtlc->jsoncmd, "htlc not found"); + command_fail(jsoncmd, "htlc not found"); return; } 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, @@ -2717,7 +2629,7 @@ static void json_failhtlc(struct command *cmd, { struct peer *peer; jsmntok_t *peeridtok, *rhashtok; - struct failhtlc *failhtlc; + struct sha256 rhash; if (!json_get_params(buffer, params, "peerid", &peeridtok, @@ -2738,20 +2650,16 @@ static void json_failhtlc(struct command *cmd, return; } - failhtlc = tal(cmd, struct failhtlc); - failhtlc->jsoncmd = cmd; - if (!hex_decode(buffer + rhashtok->start, rhashtok->end - rhashtok->start, - &failhtlc->rhash, sizeof(failhtlc->rhash))) { + &rhash, sizeof(rhash))) { command_fail(cmd, "'%.*s' is not a valid sha256 preimage", (int)(rhashtok->end - rhashtok->start), buffer + rhashtok->start); return; } - if (!queue_cmd(peer, do_failhtlc, failhtlc)) - command_fail(cmd, "Peer closing"); + do_failhtlc(peer, &rhash, cmd); } const struct json_command failhtlc_command = { @@ -2785,8 +2693,12 @@ static void json_commit(struct command *cmd, return; } - if (!queue_cmd(peer, do_commit, cmd)) - command_fail(cmd, "Peer closing"); + if (!state_can_commit(peer->state)) { + command_fail(cmd, "peer in state %s", state_name(peer->state)); + return; + } + + do_commit(peer, cmd); } const struct json_command commit_command = { @@ -2815,16 +2727,12 @@ static void json_close(struct command *cmd, 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", state_name(peer->state)); 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) set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__); else diff --git a/daemon/peer.h b/daemon/peer.h index 00a8a51e6..1160858c3 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -118,18 +118,11 @@ struct peer { /* State in state machine. */ enum state state; - /* Condition of communications */ - enum state_peercond cond; - /* Network connection. */ struct io_conn *conn; - /* Current command (or INPUT_NONE) */ - struct { - enum state_input cmd; - union input cmddata; - struct command *jsoncmd; - } curr_cmd; + /* If we're doing a commit, this is the command which triggered it */ + struct command *commit_jsoncmd; /* Pending inputs. */ 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} */ 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. */ 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, 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, const struct abs_locktime *expiry); diff --git a/daemon/test/test.sh b/daemon/test/test.sh index 6a17d928d..0475f8eea 100755 --- a/daemon/test/test.sh +++ b/daemon/test/test.sh @@ -298,18 +298,16 @@ if [ -n "$TIMEOUT_ANCHOR" ]; then MOCKTIME=$((`date +%s` + 7200 + 3 * 1200 + 1)) lcli1 dev-mocktime $MOCKTIME + lcli2 dev-mocktime $MOCKTIME - # This will crash immediately - if $LCLI2 dev-mocktime $MOCKTIME 2> /dev/null; then - echo Node2 did not crash >&2 - exit 1 - fi - $FGREP 'Entered error state STATE_ERR_ANCHOR_TIMEOUT' $DIR2/crash.log + # Node2 should have gone via STATE_ERR_ANCHOR_TIMEOUT, then closed. + lcli2 getlog | grep STATE_ERR_ANCHOR_TIMEOUT + check_no_peers lcli2 # Node1 should be disconnected. check_peerconnected lcli1 false - # It should send out commit tx; mine it. + # Node1 should send out commit tx; mine it. check_tx_spend lcli1 $CLI generate 1 @@ -335,6 +333,7 @@ if [ -n "$TIMEOUT_ANCHOR" ]; then check_no_peers lcli1 lcli1 stop + lcli2 stop all_ok fi diff --git a/names.c b/names.c index f94976eb6..47e51ce0d 100644 --- a/names.c +++ b/names.c @@ -23,26 +23,6 @@ const char *input_name(enum state_input in) 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) { size_t i; diff --git a/names.h b/names.h index e586cd774..2b0a42439 100644 --- a/names.h +++ b/names.h @@ -6,7 +6,5 @@ const char *state_name(enum state s); 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); #endif /* LIGHTNING_NAMES_H */ diff --git a/state.c b/state.c index 0cff7d8b5..5ae0ff6be 100644 --- a/state.c +++ b/state.c @@ -6,57 +6,25 @@ #include #include -static enum command_status next_state(struct peer *peer, - const enum state_input input, - enum command_status cstatus, - const enum state state) +static enum state next_state(struct peer *peer, + const enum state_input input, + const enum state state) { assert(peer->state != state); - set_peer_state(peer, state, input_name(input)); - return cstatus; + return state; } /* * Simple marker to note we don't update state. * - * This happens in three cases: - * - 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. + * This happens in normal state except when committing or closing. */ -static enum command_status unchanged_state(const struct peer *peer, - const enum state_input input, - enum command_status cstatus) +static enum state unchanged_state(const struct peer *peer, + const enum state_input input) { log_debug(peer->log, "%s: %s unchanged", input_name(input), state_name(peer->state)); - return cstatus; -} - -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; + return peer->state; } 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; } -enum command_status state(struct peer *peer, - const enum state_input input, - const union input *idata, - const struct bitcoin_tx **broadcast) +enum state state(struct peer *peer, + const enum state_input input, + const union input *idata, + const struct bitcoin_tx **broadcast) { Pkt *err; - enum command_status cstatus = CMD_NONE; *broadcast = NULL; @@ -85,14 +52,12 @@ enum command_status state(struct peer *peer, if (input_is(input, CMD_OPEN_WITH_ANCHOR)) { queue_pkt_open(peer, OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR); - change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR); } else if (input_is(input, CMD_OPEN_WITHOUT_ANCHOR)) { - change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); queue_pkt_open(peer, OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR); - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR); } break; @@ -100,12 +65,12 @@ enum command_status state(struct peer *peer, if (input_is(input, PKT_OPEN)) { err = accept_pkt_open(peer, idata->pkt); if (err) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, err->error->problem); 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)) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; @@ -113,25 +78,25 @@ enum command_status state(struct peer *peer, if (input_is(input, PKT_OPEN)) { err = accept_pkt_open(peer, idata->pkt); if (err) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, err->error->problem); goto err_breakdown; } bitcoin_create_anchor(peer, BITCOIN_ANCHOR_CREATED); - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAIT_FOR_ANCHOR_CREATE); } else if (input_is_pkt(input)) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; case STATE_OPEN_WAIT_FOR_ANCHOR_CREATE: if (input_is(input, BITCOIN_ANCHOR_CREATED)) { queue_pkt_anchor(peer); - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAIT_FOR_COMMIT_SIG); } else if (input_is_pkt(input)) { bitcoin_release_anchor(peer, BITCOIN_ANCHOR_CREATED); - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; @@ -139,7 +104,7 @@ enum command_status state(struct peer *peer, if (input_is(input, PKT_OPEN_ANCHOR)) { err = accept_pkt_anchor(peer, idata->pkt); if (err) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, err->error->problem); goto err_breakdown; } queue_pkt_open_commit_sig(peer); @@ -147,10 +112,10 @@ enum command_status state(struct peer *peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT); - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAITING_THEIRANCHOR); } else if (input_is_pkt(input)) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; @@ -159,18 +124,18 @@ enum command_status state(struct peer *peer, err = accept_pkt_open_commit_sig(peer, idata->pkt); if (err) { bitcoin_release_anchor(peer, INPUT_NONE); - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, err->error->problem); goto err_breakdown; } queue_tx_broadcast(broadcast, bitcoin_anchor(peer)); peer_watch_anchor(peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE); - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAITING_OURANCHOR); } else if (input_is_pkt(input)) { bitcoin_release_anchor(peer, INPUT_NONE); - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; @@ -178,14 +143,14 @@ enum command_status state(struct peer *peer, if (input_is(input, PKT_OPEN_COMPLETE)) { err = accept_pkt_open_complete(peer, idata->pkt); if (err) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, err->error->problem); /* We no longer care about anchor depth. */ peer_unwatch_anchor_depth(peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE); goto err_breakdown; } - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED); } /* Fall thru */ @@ -193,24 +158,24 @@ enum command_status state(struct peer *peer, if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) { queue_pkt_open_complete(peer); if (peer->state == STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED) { - complete_cmd(peer, &cstatus, CMD_SUCCESS); - return next_state(peer, input, cstatus, STATE_NORMAL); + peer_open_complete(peer, NULL); + return next_state(peer, input, STATE_NORMAL); } - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR); } else if (input_is(input, PKT_CLOSE_CLEARING)) { /* We no longer care about anchor depth. */ peer_unwatch_anchor_depth(peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE); - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "Received PKT_CLOSE_CLEARING"); goto accept_clearing; } else if (input_is_pkt(input)) { /* We no longer care about anchor depth. */ peer_unwatch_anchor_depth(peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE); - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; @@ -218,14 +183,14 @@ enum command_status state(struct peer *peer, if (input_is(input, PKT_OPEN_COMPLETE)) { err = accept_pkt_open_complete(peer, idata->pkt); if (err) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, err->error->problem); /* We no longer care about anchor depth. */ peer_unwatch_anchor_depth(peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT); goto err_breakdown; } - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED); } /* Fall thru */ @@ -233,47 +198,42 @@ enum command_status state(struct peer *peer, if (input_is(input, BITCOIN_ANCHOR_TIMEOUT)) { /* Anchor didn't reach blockchain in reasonable time. */ 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)) { queue_pkt_open_complete(peer); if (peer->state == STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED) { - complete_cmd(peer, &cstatus, CMD_SUCCESS); - return next_state(peer, input, cstatus, STATE_NORMAL); + peer_open_complete(peer, NULL); + return next_state(peer, input, STATE_NORMAL); } - return next_state(peer, input, cstatus, + return next_state(peer, input, STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR); } else if (input_is(input, PKT_CLOSE_CLEARING)) { /* We no longer care about anchor depth. */ peer_unwatch_anchor_depth(peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT); - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "Received PKT_CLOSE_CLEARING"); goto accept_clearing; } else if (input_is_pkt(input)) { /* We no longer care about anchor depth. */ peer_unwatch_anchor_depth(peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT); - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; case STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR: case STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR: if (input_is(input, PKT_OPEN_COMPLETE)) { - /* Ready for business! Anchorer goes first. */ - if (peer->state == STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR) { - complete_cmd(peer, &cstatus, CMD_SUCCESS); - return next_state(peer, input, cstatus, STATE_NORMAL); - } else { - complete_cmd(peer, &cstatus, CMD_SUCCESS); - return next_state(peer, input, cstatus, STATE_NORMAL); - } + /* Ready for business! */ + peer_open_complete(peer, NULL); + return next_state(peer, input, STATE_NORMAL); } 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; } else if (input_is_pkt(input)) { - complete_cmd(peer, &cstatus, CMD_FAIL); + peer_open_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; @@ -282,71 +242,66 @@ enum command_status state(struct peer *peer, * Channel normal operating states. */ 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. */ if (input_is(input, CMD_SEND_COMMIT)) { queue_pkt_commit(peer); - change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - return next_state(peer, input, cstatus, STATE_NORMAL_COMMITTING); - } else if (input_is(input, CMD_SEND_HTLC_ADD)) { + return next_state(peer, input, STATE_NORMAL_COMMITTING); + } + /* Fall through... */ + case STATE_NORMAL_COMMITTING: + if (input_is(input, CMD_SEND_HTLC_ADD)) { /* We are to send an HTLC add. */ 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)) { assert(idata->htlc_prog->stage.type == HTLC_FULFILL); /* We are to send an HTLC fulfill. */ queue_pkt_htlc_fulfill(peer, idata->htlc_prog->stage.fulfill.id, &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)) { assert(idata->htlc_prog->stage.type == HTLC_FAIL); /* We are to send an HTLC fail. */ 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 */ - if (peer->state == STATE_NORMAL_COMMITTING - && input_is(input, PKT_UPDATE_REVOCATION)) { + else if (peer->state == STATE_NORMAL_COMMITTING + && input_is(input, PKT_UPDATE_REVOCATION)) { err = accept_pkt_revocation(peer, idata->pkt); - if (err) { - complete_cmd(peer, &cstatus, CMD_FAIL); - goto err_breakdown; - } - complete_cmd(peer, &cstatus, CMD_SUCCESS); - return next_state(peer, input, cstatus, STATE_NORMAL); + if (err) + goto err_breakdown_maybe_committing; + peer_update_complete(peer, NULL); + return next_state(peer, input, STATE_NORMAL); } if (input_is(input, PKT_UPDATE_ADD_HTLC)) { err = accept_pkt_htlc_add(peer, idata->pkt); if (err) - goto err_breakdown; - return unchanged_state(peer, input, cstatus); + goto err_breakdown_maybe_committing; + return unchanged_state(peer, input); } else if (input_is(input, PKT_UPDATE_FULFILL_HTLC)) { err = accept_pkt_htlc_fulfill(peer, idata->pkt); if (err) - goto err_breakdown; - return unchanged_state(peer, input, cstatus); + goto err_breakdown_maybe_committing; + return unchanged_state(peer, input); } else if (input_is(input, PKT_UPDATE_FAIL_HTLC)) { err = accept_pkt_htlc_fail(peer, idata->pkt); if (err) - goto err_breakdown; - return unchanged_state(peer, input, cstatus); + goto err_breakdown_maybe_committing; + return unchanged_state(peer, input); } else if (input_is(input, PKT_UPDATE_COMMIT)) { err = accept_pkt_commit(peer, idata->pkt); if (err) - goto err_breakdown; + goto err_breakdown_maybe_committing; queue_pkt_revocation(peer); - return unchanged_state(peer, input, cstatus); + return unchanged_state(peer, input); } else if (input_is(input, PKT_CLOSE_CLEARING)) { goto accept_clearing; } else if (input_is_pkt(input)) { + if (peer->state == STATE_NORMAL_COMMITTING) + peer_update_complete(peer, "unexpected packet"); goto unexpected_pkt; } break; @@ -365,11 +320,11 @@ enum command_status state(struct peer *peer, case STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL: case STATE_CLOSE_ONCHAIN_OUR_UNILATERAL: 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. */ - return next_state(peer, input, cstatus, STATE_ERR_INTERNAL); + return next_state(peer, input, STATE_ERR_INTERNAL); unexpected_pkt: peer_unexpected_pkt(peer, idata->pkt); @@ -379,19 +334,23 @@ unexpected_pkt: goto breakdown; } 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: queue_pkt_err(peer, err); breakdown: - return next_state(peer, input, cstatus, STATE_ERR_BREAKDOWN); + return next_state(peer, input, STATE_ERR_BREAKDOWN); accept_clearing: err = accept_pkt_close_clearing(peer, idata->pkt); if (err) - goto err_breakdown; + goto err_breakdown_maybe_committing; /* If we've sent commit, we're still waiting for it when clearing. */ if (peer->state == STATE_NORMAL_COMMITTING) - return next_state(peer, input, cstatus, STATE_CLEARING_COMMITTING); - return next_state(peer, input, cstatus, STATE_CLEARING); + return next_state(peer, input, STATE_CLEARING_COMMITTING); + return next_state(peer, input, STATE_CLEARING); } diff --git a/state.h b/state.h index cdf50ddb7..efe2ca734 100644 --- a/state.h +++ b/state.h @@ -16,14 +16,61 @@ 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 bitcoin_tx; @@ -48,10 +95,10 @@ union input { } *htlc_onchain; }; -enum command_status state(struct peer *peer, - const enum state_input input, - const union input *idata, - const struct bitcoin_tx **broadcast); +enum state state(struct peer *peer, + const enum state_input input, + const union input *idata, + const struct bitcoin_tx **broadcast); /* Any CMD_SEND_HTLC_* */ #define CMD_SEND_UPDATE_ANY INPUT_MAX diff --git a/state_types.h b/state_types.h index b63706032..ab646a7f8 100644 --- a/state_types.h +++ b/state_types.h @@ -117,23 +117,4 @@ enum state_input { 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 */