diff --git a/state.c b/state.c index 73c828b82..8d6e432d8 100644 --- a/state.c +++ b/state.c @@ -25,12 +25,13 @@ static inline bool high_priority(enum state state) #define BITS_TO_STATE(bits) (STATE_CLOSED + (bits)) #define STATE_TO_BITS(state) ((state) - STATE_CLOSED) -static struct state_effect *next_state(const tal_t *ctx, - struct state_effect *effect, - const enum state state) +static enum command_status next_state(const tal_t *ctx, + enum command_status status, + struct state_effect **effect, + const enum state state) { - add_effect(&effect, new_state, state); - return effect; + add_effect(effect, new_state, state); + return status; } static void set_peer_cond(struct peer *peer, enum state_peercond cond) @@ -47,18 +48,29 @@ static void change_peer_cond(struct peer *peer, peer->cond = new; } -struct state_effect *state(const tal_t *ctx, - const enum state state, - struct peer *peer, - const enum state_input input, - const union input *idata) +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; +} + +enum command_status state(const tal_t *ctx, + const enum state state, + struct peer *peer, + const enum state_input input, + const union input *idata, + struct state_effect **effect) { - struct state_effect *effect = NULL; Pkt *decline; struct bitcoin_tx *tx; Pkt *err; struct htlc_watch *htlcs; struct htlc_progress *htlcprog; + enum command_status cstatus = CMD_NONE; + + /* NULL-terminated linked list. */ + *effect = NULL; switch (state) { /* @@ -66,65 +78,65 @@ struct state_effect *state(const tal_t *ctx, */ case STATE_INIT: if (input_is(input, CMD_OPEN_WITH_ANCHOR)) { - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_open(ctx, peer, OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR)); change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR); } else if (input_is(input, CMD_OPEN_WITHOUT_ANCHOR)) { change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_open(ctx, peer, OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR)); - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR); } break; case STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR: if (input_is(input, PKT_OPEN)) { - err = accept_pkt_open(ctx, peer, idata->pkt, &effect); + err = accept_pkt_open(ctx, peer, idata->pkt, effect); if (err) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto err_close_nocleanup; } - return next_state(ctx, effect, STATE_OPEN_WAIT_FOR_ANCHOR); + return next_state(ctx, cstatus, effect, STATE_OPEN_WAIT_FOR_ANCHOR); } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto instant_close; } else if (input_is_pkt(input)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt_nocleanup; } break; case STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR: if (input_is(input, PKT_OPEN)) { - err = accept_pkt_open(ctx, peer, idata->pkt, &effect); + err = accept_pkt_open(ctx, peer, idata->pkt, effect); if (err) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto err_close_nocleanup; } - add_effect(&effect, send_pkt, pkt_anchor(ctx, peer)); - return next_state(ctx, effect, + add_effect(effect, send_pkt, pkt_anchor(ctx, peer)); + return next_state(ctx, cstatus, effect, STATE_OPEN_WAIT_FOR_COMMIT_SIG); } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto instant_close; } else if (input_is_pkt(input)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt_nocleanup; } break; case STATE_OPEN_WAIT_FOR_ANCHOR: if (input_is(input, PKT_OPEN_ANCHOR)) { - err = accept_pkt_anchor(ctx, peer, idata->pkt, &effect); + err = accept_pkt_anchor(ctx, peer, idata->pkt, effect); if (err) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto err_close_nocleanup; } - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_open_commit_sig(ctx, peer)); - add_effect(&effect, watch, + add_effect(effect, watch, bitcoin_watch_anchor(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT, @@ -132,160 +144,160 @@ struct state_effect *state(const tal_t *ctx, BITCOIN_ANCHOR_THEIRSPEND, BITCOIN_ANCHOR_OTHERSPEND)); - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_OPEN_WAITING_THEIRANCHOR); } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto instant_close; } else if (input_is_pkt(input)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt_nocleanup; } break; case STATE_OPEN_WAIT_FOR_COMMIT_SIG: if (input_is(input, PKT_OPEN_COMMIT_SIG)) { err = accept_pkt_open_commit_sig(ctx, peer, idata->pkt, - &effect); + effect); if (err) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto err_start_unilateral_close; } - add_effect(&effect, broadcast_tx, + add_effect(effect, broadcast_tx, bitcoin_anchor(ctx, peer)); - add_effect(&effect, watch, + add_effect(effect, watch, bitcoin_watch_anchor(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE, BITCOIN_ANCHOR_UNSPENT, BITCOIN_ANCHOR_THEIRSPEND, BITCOIN_ANCHOR_OTHERSPEND)); - return next_state(ctx, effect, STATE_OPEN_WAITING_OURANCHOR); + return next_state(ctx, cstatus, effect, + STATE_OPEN_WAITING_OURANCHOR); } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto instant_close; } else if (input_is_pkt(input)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt_nocleanup; } break; case STATE_OPEN_WAITING_OURANCHOR: if (input_is(input, PKT_OPEN_COMPLETE)) { - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED); } /* Fall thru */ case STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED: if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) { - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_open_complete(ctx, peer)); if (state == STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED) { - change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK); - add_effect(&effect, cmd_success, CMD_OPEN_WITH_ANCHOR); - return next_state(ctx, effect, STATE_NORMAL_HIGHPRIO); + complete_cmd(peer, &cstatus, CMD_SUCCESS); + return next_state(ctx, cstatus, effect, + STATE_NORMAL_HIGHPRIO); } - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { /* This should be impossible. */ - return next_state(ctx, effect, STATE_ERR_INFORMATION_LEAK); + return next_state(ctx, cstatus, effect, STATE_ERR_INFORMATION_LEAK); } else if (input_is(input, CMD_CLOSE)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto start_closing; } else if (input_is(input, PKT_CLOSE)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto accept_closing; } else if (input_is_pkt(input)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt_nocleanup; } break; case STATE_OPEN_WAITING_THEIRANCHOR: if (input_is(input, PKT_OPEN_COMPLETE)) { - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED); } /* Fall thru */ case STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED: if (input_is(input, BITCOIN_ANCHOR_TIMEOUT)) { /* Anchor didn't reach blockchain in reasonable time. */ - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_err(ctx, "Anchor timed out")); - return next_state(ctx, effect, STATE_ERR_ANCHOR_TIMEOUT); + return next_state(ctx, cstatus, effect, STATE_ERR_ANCHOR_TIMEOUT); } else if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) { - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_open_complete(ctx, peer)); if (state == STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED) { - add_effect(&effect, cmd_success, - CMD_OPEN_WITHOUT_ANCHOR); - change_peer_cond(peer, PEER_BUSY, - PEER_CMD_OK); - return next_state(ctx, effect, + complete_cmd(peer, &cstatus, CMD_SUCCESS); + return next_state(ctx, cstatus, effect, STATE_NORMAL_LOWPRIO); } - return next_state(ctx, effect, STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR); + return next_state(ctx, cstatus, effect, + STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { /* This should be impossible. */ - return next_state(ctx, effect, STATE_ERR_INFORMATION_LEAK); + return next_state(ctx, cstatus, effect, + STATE_ERR_INFORMATION_LEAK); } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto them_unilateral; } else if (input_is(input, CMD_CLOSE)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto start_closing; } else if (input_is(input, PKT_CLOSE)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto accept_closing; } else if (input_is_pkt(input)) { /* We no longer care about anchor depth. */ - add_effect(&effect, unwatch, + add_effect(effect, unwatch, bitcoin_unwatch_anchor_depth(ctx, peer, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt; } break; @@ -293,32 +305,34 @@ struct state_effect *state(const tal_t *ctx, case STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR: if (input_is(input, PKT_OPEN_COMPLETE)) { /* Ready for business! Anchorer goes first. */ - change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK); if (state == STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR) { - add_effect(&effect, cmd_success, CMD_OPEN_WITH_ANCHOR); - return next_state(ctx, effect, STATE_NORMAL_HIGHPRIO); + complete_cmd(peer, &cstatus, CMD_SUCCESS); + return next_state(ctx, cstatus, effect, + STATE_NORMAL_HIGHPRIO); } else { - add_effect(&effect, cmd_success, CMD_OPEN_WITHOUT_ANCHOR); - return next_state(ctx, effect, STATE_NORMAL_LOWPRIO); + complete_cmd(peer, &cstatus, CMD_SUCCESS); + return next_state(ctx, cstatus, effect, + STATE_NORMAL_LOWPRIO); } } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto anchor_unspent; /* Nobody should be able to spend anchor, except via the * commit txs. */ } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - return next_state(ctx, effect, STATE_ERR_INFORMATION_LEAK); + return next_state(ctx, cstatus, effect, + STATE_ERR_INFORMATION_LEAK); } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto them_unilateral; } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto start_closing; } else if (input_is(input, PKT_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto accept_closing; } else if (input_is_pkt(input)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt; } break; @@ -331,42 +345,42 @@ struct state_effect *state(const tal_t *ctx, assert(peer->cond == PEER_CMD_OK); if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* We are to send an HTLC update. */ - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_htlc_update(ctx, peer, idata->htlc_prog)); - add_effect(&effect, htlc_in_progress, idata->htlc_prog); + add_effect(effect, htlc_in_progress, idata->htlc_prog); change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, prio(state, STATE_WAIT_FOR_HTLC_ACCEPT)); } else if (input_is(input, CMD_SEND_HTLC_FULFILL)) { /* We are to send an HTLC fulfill. */ /* This gives us the r value (FIXME: type!) */ - add_effect(&effect, r_value, + add_effect(effect, r_value, r_value_from_cmd(ctx, peer, idata->htlc)); - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_htlc_fulfill(ctx, peer, idata->htlc_prog)); - add_effect(&effect, htlc_in_progress, idata->htlc_prog); + add_effect(effect, htlc_in_progress, idata->htlc_prog); change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT)); } else if (input_is(input, CMD_SEND_HTLC_TIMEDOUT)) { /* We are to send an HTLC timedout. */ - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_htlc_timedout(ctx, peer, idata->htlc_prog)); - add_effect(&effect, htlc_in_progress, idata->htlc_prog); + add_effect(effect, htlc_in_progress, idata->htlc_prog); change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT)); } else if (input_is(input, CMD_SEND_HTLC_ROUTEFAIL)) { /* We are to send an HTLC routefail. */ - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_htlc_routefail(ctx, peer, idata->htlc_prog)); - add_effect(&effect, htlc_in_progress, idata->htlc_prog); + add_effect(effect, htlc_in_progress, idata->htlc_prog); change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT)); } else if (input_is(input, CMD_CLOSE)) { goto start_closing; @@ -398,16 +412,15 @@ struct state_effect *state(const tal_t *ctx, case STATE_WAIT_FOR_HTLC_ACCEPT_HIGHPRIO: /* HTLCs can also evoke a refusal. */ if (input_is(input, PKT_UPDATE_DECLINE_HTLC)) { - add_effect(&effect, cmd_fail, idata->pkt); - add_effect(&effect, htlc_abandon, true); - change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK); + add_effect(effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); /* No update means no priority change. */ - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, prio(state, STATE_NORMAL)); /* They can't close with an HTLC, so only possible here */ } else if (input_is(input, PKT_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); + add_effect(effect, htlc_abandon, true); goto accept_closing; } /* Fall thru */ @@ -416,82 +429,82 @@ struct state_effect *state(const tal_t *ctx, if (input_is(input, PKT_UPDATE_ADD_HTLC)) { /* If we're high priority, ignore their packet */ if (high_priority(state)) - return effect; + return cstatus; /* Otherwise, process their request first: defer ours */ - add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_REQUEUE); + add_effect(effect, htlc_abandon, true); /* Stay busy, since we're processing theirs. */ - assert(peer->cond == PEER_BUSY); + change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); goto accept_htlc_update; } else if (input_is(input, PKT_UPDATE_FULFILL_HTLC)) { /* If we're high priority, ignore their packet */ if (high_priority(state)) - return effect; + return cstatus; /* Otherwise, process their request first: defer ours */ - add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_REQUEUE); + add_effect(effect, htlc_abandon, true); /* Stay busy, since we're processing theirs. */ - assert(peer->cond == PEER_BUSY); + change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); goto accept_htlc_fulfill; } else if (input_is(input, PKT_UPDATE_TIMEDOUT_HTLC)) { /* If we're high priority, ignore their packet */ if (high_priority(state)) - return effect; + return cstatus; /* Otherwise, process their request first: defer ours */ - add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_REQUEUE); + add_effect(effect, htlc_abandon, true); /* Stay busy, since we're processing theirs. */ - assert(peer->cond == PEER_BUSY); + change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); goto accept_htlc_timedout; } else if (input_is(input, PKT_UPDATE_ROUTEFAIL_HTLC)) { /* If we're high priority, ignore their packet */ if (high_priority(state)) - return effect; + return cstatus; /* Otherwise, process their request first: defer ours */ - add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_REQUEUE); + add_effect(effect, htlc_abandon, true); /* Stay busy, since we're processing theirs. */ - assert(peer->cond == PEER_BUSY); + change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY); goto accept_htlc_routefail; } else if (input_is(input, PKT_UPDATE_ACCEPT)) { struct signature *sig; err = accept_pkt_update_accept(ctx, peer, idata->pkt, - &sig, &effect); + &sig, effect); if (err) { - add_effect(&effect, cmd_fail, NULL); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); + add_effect(effect, htlc_abandon, true); goto err_start_unilateral_close; } - add_effect(&effect, update_theirsig, sig); - add_effect(&effect, send_pkt, + add_effect(effect, update_theirsig, sig); + add_effect(effect, send_pkt, pkt_update_signature(ctx, peer)); /* HTLC is signed (though old tx not revoked yet!) */ - add_effect(&effect, htlc_fulfill, true); - return next_state(ctx, effect, + add_effect(effect, htlc_fulfill, true); + return next_state(ctx, cstatus, effect, prio(state, STATE_WAIT_FOR_UPDATE_COMPLETE)); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - add_effect(&effect, cmd_fail, NULL); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); + add_effect(effect, htlc_abandon, true); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - add_effect(&effect, cmd_fail, NULL); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); + add_effect(effect, htlc_abandon, true); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - add_effect(&effect, cmd_fail, NULL); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); + add_effect(effect, htlc_abandon, true); goto old_commit_spotted; } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); + add_effect(effect, htlc_abandon, true); goto start_closing; } else if (input_is_pkt(input)) { - add_effect(&effect, cmd_fail, NULL); - add_effect(&effect, htlc_abandon, true); + complete_cmd(peer, &cstatus, CMD_FAIL); + add_effect(effect, htlc_abandon, true); goto unexpected_pkt; } break; @@ -499,32 +512,31 @@ struct state_effect *state(const tal_t *ctx, case STATE_WAIT_FOR_UPDATE_COMPLETE_HIGHPRIO: if (input_is(input, PKT_UPDATE_COMPLETE)) { err = accept_pkt_update_complete(ctx, peer, idata->pkt, - &effect); + effect); if (err) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto err_start_unilateral_close; } - add_effect(&effect, cmd_success, CMD_SEND_UPDATE_ANY); - change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK); - return next_state(ctx, effect, + complete_cmd(peer, &cstatus, CMD_SUCCESS); + return next_state(ctx, cstatus, effect, toggle_prio(state, STATE_NORMAL)); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto old_commit_spotted; } else if (input_is(input, PKT_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto accept_closing; } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto start_closing; } else if (input_is_pkt(input)) { - add_effect(&effect, cmd_fail, NULL); + complete_cmd(peer, &cstatus, CMD_FAIL); goto unexpected_pkt; } break; @@ -533,36 +545,36 @@ struct state_effect *state(const tal_t *ctx, if (input_is(input, PKT_UPDATE_SIGNATURE)) { struct signature *sig; err = accept_pkt_update_signature(ctx, peer, idata->pkt, - &sig, &effect); + &sig, effect); if (err) { - add_effect(&effect, htlc_abandon, true); + add_effect(effect, htlc_abandon, true); goto err_start_unilateral_close; } - add_effect(&effect, update_theirsig, sig); - add_effect(&effect, send_pkt, + add_effect(effect, update_theirsig, sig); + add_effect(effect, send_pkt, pkt_update_complete(ctx, peer)); - add_effect(&effect, htlc_fulfill, true); + add_effect(effect, htlc_fulfill, true); change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK); /* Toggle between high and low priority states. */ - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, toggle_prio(state, STATE_NORMAL)); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - add_effect(&effect, htlc_abandon, true); + add_effect(effect, htlc_abandon, true); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - add_effect(&effect, htlc_abandon, true); + add_effect(effect, htlc_abandon, true); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - add_effect(&effect, htlc_abandon, true); + add_effect(effect, htlc_abandon, true); goto old_commit_spotted; } else if (input_is(input, CMD_CLOSE)) { - add_effect(&effect, htlc_abandon, true); + add_effect(effect, htlc_abandon, true); goto start_closing; } else if (input_is(input, PKT_CLOSE)) { - add_effect(&effect, htlc_abandon, true); + add_effect(effect, htlc_abandon, true); goto accept_closing; } else if (input_is_pkt(input)) { - add_effect(&effect, htlc_abandon, true); + add_effect(effect, htlc_abandon, true); goto unexpected_pkt; } break; @@ -570,69 +582,67 @@ struct state_effect *state(const tal_t *ctx, case STATE_WAIT_FOR_CLOSE_COMPLETE: if (input_is(input, PKT_CLOSE_COMPLETE)) { err = accept_pkt_close_complete(ctx, peer, idata->pkt, - &effect); + effect); if (err) goto err_start_unilateral_close_already_closing; - add_effect(&effect, cmd_close_done, true); - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_close_ack(ctx, peer)); - add_effect(&effect, broadcast_tx, + add_effect(effect, broadcast_tx, bitcoin_close(ctx, peer)); change_peer_cond(peer, PEER_CLOSING, PEER_CLOSED); - return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_CLOSE); } else if (input_is(input, PKT_CLOSE)) { /* We can use the sig just like CLOSE_COMPLETE */ err = accept_pkt_simultaneous_close(ctx, peer, idata->pkt, - &effect); + effect); if (err) goto err_start_unilateral_close_already_closing; - add_effect(&effect, cmd_close_done, true); - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, pkt_close_ack(ctx, peer)); - add_effect(&effect, broadcast_tx, + add_effect(effect, broadcast_tx, bitcoin_close(ctx, peer)); set_peer_cond(peer, PEER_CLOSED); - return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_CLOSE); } else if (input_is(input, PKT_ERROR)) { - add_effect(&effect, in_error, + add_effect(effect, in_error, tal_steal(ctx, idata->pkt)); goto start_unilateral_close_already_closing; } else if (input_is_pkt(input)) { /* We ignore all other packets while closing. */ - return next_state(ctx, effect, STATE_WAIT_FOR_CLOSE_COMPLETE); + return next_state(ctx, cstatus, effect, + STATE_WAIT_FOR_CLOSE_COMPLETE); } else if (input_is(input, INPUT_CLOSE_COMPLETE_TIMEOUT)) { /* They didn't respond in time. Unilateral close. */ err = pkt_err(ctx, "Close timed out"); goto err_start_unilateral_close_already_closing; } - add_effect(&effect, cmd_close_done, false); goto fail_during_close; case STATE_WAIT_FOR_CLOSE_ACK: if (input_is(input, PKT_CLOSE_ACK)) { err = accept_pkt_close_ack(ctx, peer, idata->pkt, - &effect); + effect); if (err) - add_effect(&effect, send_pkt, err); + add_effect(effect, send_pkt, err); set_peer_cond(peer, PEER_CLOSED); /* Just wait for close to happen now. */ - return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_CLOSE); } else if (input_is_pkt(input)) { if (input_is(input, PKT_ERROR)) { - add_effect(&effect, in_error, + add_effect(effect, in_error, tal_steal(ctx, idata->pkt)); } else { - add_effect(&effect, send_pkt, + add_effect(effect, send_pkt, unexpected_pkt(ctx, input)); } set_peer_cond(peer, PEER_CLOSED); /* Just wait for close to happen now. */ - return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_CLOSE); } else if (input_is(input, BITCOIN_CLOSE_DONE)) { /* They didn't ack, but we're closed, so stop. */ set_peer_cond(peer, PEER_CLOSED); - return next_state(ctx, effect, STATE_CLOSED); + return next_state(ctx, cstatus, effect, STATE_CLOSED); } goto fail_during_close; @@ -694,23 +704,23 @@ struct state_effect *state(const tal_t *ctx, /* One a steal is complete, we don't care about htlcs * (we stole them all) */ if (bits & STATE_CLOSE_HTLCS_BIT) - add_effect(&effect, unwatch_htlc, + add_effect(effect, unwatch_htlc, htlc_unwatch_all(ctx, peer)); - return next_state(ctx, effect, STATE_CLOSED); + return next_state(ctx, cstatus, effect, STATE_CLOSED); } if ((bits & STATE_CLOSE_SPENDTHEM_BIT) && input_is(input, BITCOIN_SPEND_THEIRS_DONE)) { BUILD_ASSERT(!(STATE_TO_BITS(STATE_CLOSE_WAIT_HTLCS) & STATE_CLOSE_SPENDTHEM_BIT)); - return next_state(ctx, effect, closed); + return next_state(ctx, cstatus, effect, closed); } if ((bits & STATE_CLOSE_CLOSE_BIT) && input_is(input, BITCOIN_CLOSE_DONE)) { BUILD_ASSERT(!(STATE_TO_BITS(STATE_CLOSE_WAIT_HTLCS) & STATE_CLOSE_CLOSE_BIT)); - return next_state(ctx, effect, closed); + return next_state(ctx, cstatus, effect, closed); } if ((bits & STATE_CLOSE_OURCOMMIT_BIT) @@ -719,20 +729,20 @@ struct state_effect *state(const tal_t *ctx, & STATE_CLOSE_OURCOMMIT_BIT)); tx = bitcoin_spend_ours(ctx, peer); /* Now we need to wait for our commit to be done. */ - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch(ctx, tx, BITCOIN_SPEND_OURS_DONE)); bits &= ~STATE_CLOSE_OURCOMMIT_BIT; bits |= STATE_CLOSE_SPENDOURS_BIT; - return next_state(ctx, effect, BITS_TO_STATE(bits)); + return next_state(ctx, cstatus, effect, BITS_TO_STATE(bits)); } if ((bits & STATE_CLOSE_SPENDOURS_BIT) && input_is(input, BITCOIN_SPEND_OURS_DONE)) { BUILD_ASSERT(!(STATE_TO_BITS(STATE_CLOSE_WAIT_HTLCS) & STATE_CLOSE_SPENDOURS_BIT)); - return next_state(ctx, effect, closed); + return next_state(ctx, cstatus, effect, closed); } /* If we have htlcs, we can get other inputs... */ @@ -741,82 +751,83 @@ struct state_effect *state(const tal_t *ctx, /* Clear bit, might lead to STATE_CLOSED. */ BUILD_ASSERT((BITS_TO_STATE(STATE_TO_BITS(STATE_CLOSE_WAIT_HTLCS) & ~STATE_CLOSE_HTLCS_BIT)) == STATE_CLOSED); bits &= ~STATE_CLOSE_HTLCS_BIT; - return next_state(ctx, effect, BITS_TO_STATE(bits)); + return next_state(ctx, cstatus, effect, + BITS_TO_STATE(bits)); } else if (input_is(input, BITCOIN_HTLC_TOTHEM_SPENT)) { /* They revealed R value. */ - add_effect(&effect, r_value, + add_effect(effect, r_value, bitcoin_r_value(ctx, idata->htlc)); /* We don't care any more. */ - add_effect(&effect, unwatch_htlc, + add_effect(effect, unwatch_htlc, htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return effect; + return cstatus; } else if (input_is(input, BITCOIN_HTLC_TOTHEM_TIMEOUT)){ tx = bitcoin_htlc_timeout(ctx, peer, idata->htlc); /* HTLC timed out, spend it back to us. */ - add_effect(&effect, broadcast_tx, tx); + add_effect(effect, broadcast_tx, tx); /* Don't unwatch yet; they could yet * try to spend, revealing rvalue. */ /* We're done when that gets buried. */ - add_effect(&effect, watch_htlc_spend, + add_effect(effect, watch_htlc_spend, htlc_spend_watch(ctx, tx, idata->cmd, BITCOIN_HTLC_RETURN_SPEND_DONE)); - return effect; + return cstatus; } else if (input_is(input, INPUT_RVALUE)) { tx = bitcoin_htlc_spend(ctx, peer, idata->htlc); /* This gives us the r value. */ - add_effect(&effect, r_value, + add_effect(effect, r_value, r_value_from_cmd(ctx, peer, idata->htlc)); /* Spend it... */ - add_effect(&effect, broadcast_tx, tx); + add_effect(effect, broadcast_tx, tx); /* We're done when it gets buried. */ - add_effect(&effect, watch_htlc_spend, + add_effect(effect, watch_htlc_spend, htlc_spend_watch(ctx, tx, idata->cmd, BITCOIN_HTLC_FULFILL_SPEND_DONE)); /* Don't care about this one any more. */ - add_effect(&effect, unwatch_htlc, + add_effect(effect, unwatch_htlc, htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return effect; + return cstatus; } else if (input_is(input, BITCOIN_HTLC_FULFILL_SPEND_DONE)) { /* Stop watching spend, send * INPUT_NO_MORE_HTLCS when done. */ - add_effect(&effect, unwatch_htlc_spend, + add_effect(effect, unwatch_htlc_spend, htlc_spend_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return effect; + return cstatus; } else if (input_is(input, BITCOIN_HTLC_RETURN_SPEND_DONE)) { /* Stop watching spend, send * INPUT_NO_MORE_HTLCS when done. */ - add_effect(&effect, unwatch_htlc_spend, + add_effect(effect, unwatch_htlc_spend, htlc_spend_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); /* Don't need to watch the HTLC output any more, * either. */ - add_effect(&effect, unwatch_htlc, + add_effect(effect, unwatch_htlc, htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return effect; + return cstatus; } else if (input_is(input, BITCOIN_HTLC_TOUS_TIMEOUT)) { /* They can spend, we no longer care * about this HTLC. */ - add_effect(&effect, unwatch_htlc, + add_effect(effect, unwatch_htlc, htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return effect; + return cstatus; } } @@ -830,8 +841,8 @@ struct state_effect *state(const tal_t *ctx, */ if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { tx = bitcoin_spend_theirs(ctx, peer, idata->btc); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch(ctx, tx, BITCOIN_SPEND_THEIRS_DONE)); /* HTLC watches. */ @@ -841,23 +852,25 @@ struct state_effect *state(const tal_t *ctx, BITCOIN_HTLC_TOTHEM_SPENT, BITCOIN_HTLC_TOTHEM_TIMEOUT); if (htlcs) { - add_effect(&effect, watch_htlcs, htlcs); + add_effect(effect, watch_htlcs, htlcs); bits |= STATE_CLOSE_HTLCS_BIT; } bits |= STATE_CLOSE_SPENDTHEM_BIT; - return next_state(ctx, effect, BITS_TO_STATE(bits)); + return next_state(ctx, cstatus, effect, + BITS_TO_STATE(bits)); /* This can happen multiple times: need to steal ALL */ } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { tx = bitcoin_steal(ctx, peer, idata->btc); if (!tx) - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_ERR_INFORMATION_LEAK); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch(ctx, tx, BITCOIN_STEAL_DONE)); bits |= STATE_CLOSE_STEAL_BIT; - return next_state(ctx, effect, BITS_TO_STATE(bits)); + return next_state(ctx, cstatus, effect, + BITS_TO_STATE(bits)); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) goto anchor_unspent; @@ -878,11 +891,11 @@ struct state_effect *state(const tal_t *ctx, case STATE_UNUSED_CLOSE_WAIT_STEAL_CLOSE_OURCOMMIT_WITH_HTLCS: case STATE_UNUSED_CLOSE_WAIT_CLOSE_SPENDOURS_WITH_HTLCS: case STATE_UNUSED_CLOSE_WAIT_STEAL_CLOSE_SPENDOURS_WITH_HTLCS: - return next_state(ctx, effect, STATE_ERR_INTERNAL); + return next_state(ctx, cstatus, effect, STATE_ERR_INTERNAL); } /* State machine should handle all possible states. */ - return next_state(ctx, effect, STATE_ERR_INTERNAL); + return next_state(ctx, cstatus, effect, STATE_ERR_INTERNAL); unexpected_pkt: /* @@ -890,7 +903,7 @@ unexpected_pkt: */ /* Don't reply to an error with an error. */ if (input_is(input, PKT_ERROR)) { - add_effect(&effect, in_error, tal_steal(ctx, idata->pkt)); + add_effect(effect, in_error, tal_steal(ctx, idata->pkt)); goto start_unilateral_close; } err = unexpected_pkt(ctx, input); @@ -909,22 +922,22 @@ anchor_unspent: * then we're malfunctioning. If they double-spent it, then they * managed to cheat us: post_to_reddit(); */ - return next_state(ctx, effect, STATE_ERR_ANCHOR_LOST); + return next_state(ctx, cstatus, effect, STATE_ERR_ANCHOR_LOST); err_close_nocleanup: /* * Something went wrong, but we haven't sent anything to the blockchain * so there's nothing to clean up. */ - add_effect(&effect, send_pkt, err); - set_peer_cond(peer, PEER_CLOSED); - return next_state(ctx, effect, STATE_CLOSED); + add_effect(effect, send_pkt, err); + change_peer_cond(peer, PEER_CMD_OK, PEER_CLOSED); + return next_state(ctx, cstatus, effect, STATE_CLOSED); err_start_unilateral_close: /* * They timed out, or were broken; we are going to close unilaterally. */ - add_effect(&effect, send_pkt, err); + add_effect(effect, send_pkt, err); start_unilateral_close: /* @@ -933,8 +946,8 @@ start_unilateral_close: /* No more inputs, no more commands. */ set_peer_cond(peer, PEER_CLOSED); tx = bitcoin_commit(ctx, peer); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch_delayed(ctx, tx, BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED)); @@ -944,37 +957,35 @@ start_unilateral_close: BITCOIN_HTLC_TOTHEM_SPENT, BITCOIN_HTLC_TOTHEM_TIMEOUT); if (htlcs) { - add_effect(&effect, watch_htlcs, htlcs); - return next_state(ctx, effect, + add_effect(effect, watch_htlcs, htlcs); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_OURCOMMIT_WITH_HTLCS); } - return next_state(ctx, effect, STATE_CLOSE_WAIT_OURCOMMIT); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_OURCOMMIT); err_start_unilateral_close_already_closing: /* * They timed out, or were broken; we are going to close unilaterally. */ - add_effect(&effect, send_pkt, err); + add_effect(effect, send_pkt, err); start_unilateral_close_already_closing: - add_effect(&effect, cmd_close_done, false); - /* * Close unilaterally. */ /* No more inputs, no more commands. */ set_peer_cond(peer, PEER_CLOSED); tx = bitcoin_commit(ctx, peer); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch_delayed(ctx, tx, BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED)); /* We agreed to close: shouldn't have any HTLCs */ if (committed_to_htlcs(peer)) - return next_state(ctx, effect, STATE_ERR_INTERNAL); + return next_state(ctx, cstatus, effect, STATE_ERR_INTERNAL); - return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE_OURCOMMIT); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_CLOSE_OURCOMMIT); them_unilateral: assert(input == BITCOIN_ANCHOR_THEIRSPEND); @@ -982,13 +993,13 @@ them_unilateral: /* * Bitcoind tells us they did unilateral close. */ - add_effect(&effect, send_pkt, pkt_err(ctx, "Commit tx noticed")); + add_effect(effect, send_pkt, pkt_err(ctx, "Commit tx noticed")); /* No more inputs, no more commands. */ set_peer_cond(peer, PEER_CLOSED); tx = bitcoin_spend_theirs(ctx, peer, idata->btc); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch(ctx, tx, BITCOIN_SPEND_THEIRS_DONE)); @@ -999,53 +1010,59 @@ them_unilateral: BITCOIN_HTLC_TOTHEM_SPENT, BITCOIN_HTLC_TOTHEM_TIMEOUT); if (htlcs) { - add_effect(&effect, watch_htlcs, htlcs); - return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM_WITH_HTLCS); + add_effect(effect, watch_htlcs, htlcs); + return next_state(ctx, cstatus, effect, + STATE_CLOSE_WAIT_SPENDTHEM_WITH_HTLCS); } - return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_SPENDTHEM); accept_htlc_update: err = accept_pkt_htlc_update(ctx, peer, idata->pkt, &decline, &htlcprog, - &effect); + effect); if (err) goto err_start_unilateral_close; if (decline) { - add_effect(&effect, send_pkt, decline); + add_effect(effect, send_pkt, decline); /* No update means no priority change. */ change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK); - return next_state(ctx, effect, prio(state, STATE_NORMAL)); + return next_state(ctx, cstatus, effect, + prio(state, STATE_NORMAL)); } - add_effect(&effect, htlc_in_progress, htlcprog); - add_effect(&effect, send_pkt, pkt_update_accept(ctx, peer)); - return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); + add_effect(effect, htlc_in_progress, htlcprog); + add_effect(effect, send_pkt, pkt_update_accept(ctx, peer)); + return next_state(ctx, cstatus, effect, + prio(state, STATE_WAIT_FOR_UPDATE_SIG)); accept_htlc_routefail: err = accept_pkt_htlc_routefail(ctx, peer, idata->pkt, &htlcprog, - &effect); + effect); if (err) goto err_start_unilateral_close; - add_effect(&effect, htlc_in_progress, htlcprog); - add_effect(&effect, send_pkt, pkt_update_accept(ctx, peer)); - return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); + add_effect(effect, htlc_in_progress, htlcprog); + add_effect(effect, send_pkt, pkt_update_accept(ctx, peer)); + return next_state(ctx, cstatus, effect, + prio(state, STATE_WAIT_FOR_UPDATE_SIG)); accept_htlc_timedout: err = accept_pkt_htlc_timedout(ctx, peer, idata->pkt, &htlcprog, - &effect); + effect); if (err) goto err_start_unilateral_close; - add_effect(&effect, htlc_in_progress, htlcprog); - add_effect(&effect, send_pkt, pkt_update_accept(ctx, peer)); - return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); + add_effect(effect, htlc_in_progress, htlcprog); + add_effect(effect, send_pkt, pkt_update_accept(ctx, peer)); + return next_state(ctx, cstatus, effect, + prio(state, STATE_WAIT_FOR_UPDATE_SIG)); accept_htlc_fulfill: err = accept_pkt_htlc_fulfill(ctx, peer, idata->pkt, &htlcprog, - &effect); + effect); if (err) goto err_start_unilateral_close; - add_effect(&effect, htlc_in_progress, htlcprog); - add_effect(&effect, send_pkt, pkt_update_accept(ctx, peer)); - add_effect(&effect, r_value, r_value_from_pkt(ctx, idata->pkt)); - return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); + add_effect(effect, htlc_in_progress, htlcprog); + add_effect(effect, send_pkt, pkt_update_accept(ctx, peer)); + add_effect(effect, r_value, r_value_from_pkt(ctx, idata->pkt)); + return next_state(ctx, cstatus, effect, + prio(state, STATE_WAIT_FOR_UPDATE_SIG)); start_closing: /* @@ -1053,47 +1070,45 @@ start_closing: */ /* Protocol doesn't (currently?) allow closing with HTLCs. */ if (committed_to_htlcs(peer)) { - add_effect(&effect, cmd_close_done, false); err = pkt_err(ctx, "Close forced due to HTLCs"); goto err_start_unilateral_close; } - add_effect(&effect, close_timeout, INPUT_CLOSE_COMPLETE_TIMEOUT); + add_effect(effect, close_timeout, INPUT_CLOSE_COMPLETE_TIMEOUT); - add_effect(&effect, watch, + add_effect(effect, watch, bitcoin_watch_close(ctx, peer, BITCOIN_CLOSE_DONE)); /* No more commands, we're already closing. */ set_peer_cond(peer, PEER_CLOSING); /* As soon as we send packet, they could close. */ - add_effect(&effect, send_pkt, pkt_close(ctx, peer)); - return next_state(ctx, effect, STATE_WAIT_FOR_CLOSE_COMPLETE); + add_effect(effect, send_pkt, pkt_close(ctx, peer)); + return next_state(ctx, cstatus, effect, STATE_WAIT_FOR_CLOSE_COMPLETE); accept_closing: - err = accept_pkt_close(ctx, peer, idata->pkt, &effect); + err = accept_pkt_close(ctx, peer, idata->pkt, effect); if (err) goto err_start_unilateral_close; /* As soon as we send packet, they could close. */ - add_effect(&effect, watch, + add_effect(effect, watch, bitcoin_watch_close(ctx, peer, BITCOIN_CLOSE_DONE)); - add_effect(&effect, send_pkt, pkt_close_complete(ctx, peer)); + add_effect(effect, send_pkt, pkt_close_complete(ctx, peer)); /* No more commands, we're already closing. */ set_peer_cond(peer, PEER_CLOSING); - return next_state(ctx, effect, STATE_WAIT_FOR_CLOSE_ACK); + return next_state(ctx, cstatus, effect, STATE_WAIT_FOR_CLOSE_ACK); instant_close: /* * Closing, but we haven't sent anything to the blockchain so * there's nothing to clean up. */ - add_effect(&effect, cmd_close_done, true); /* FIXME: Should we tell other side we're going? */ set_peer_cond(peer, PEER_CLOSED); /* We can't have any HTLCs, since we haven't started. */ if (committed_to_htlcs(peer)) - return next_state(ctx, effect, STATE_ERR_INTERNAL); - return next_state(ctx, effect, STATE_CLOSED); + return next_state(ctx, cstatus, effect, STATE_ERR_INTERNAL); + return next_state(ctx, cstatus, effect, STATE_CLOSED); fail_during_close: /* @@ -1104,12 +1119,12 @@ fail_during_close: /* Once close tx is deep enough, we consider it done. */ if (input_is(input, BITCOIN_CLOSE_DONE)) { - return next_state(ctx, effect, STATE_CLOSED); + return next_state(ctx, cstatus, effect, STATE_CLOSED); } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { /* A reorganization could make this happen. */ tx = bitcoin_spend_theirs(ctx, peer, idata->btc); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch(ctx, tx, BITCOIN_SPEND_THEIRS_DONE)); htlcs = htlc_outputs_their_commit(ctx, peer, idata->btc, @@ -1120,30 +1135,33 @@ fail_during_close: if (htlcs) { /* FIXME: Make sure caller uses INPUT_RVAL * if they were in the middle of FULFILL! */ - add_effect(&effect, watch_htlcs, htlcs); - return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM_CLOSE_WITH_HTLCS); + add_effect(effect, watch_htlcs, htlcs); + return next_state(ctx, cstatus, effect, + STATE_CLOSE_WAIT_SPENDTHEM_CLOSE_WITH_HTLCS); } - return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM_CLOSE); + return next_state(ctx, cstatus, effect, + STATE_CLOSE_WAIT_SPENDTHEM_CLOSE); } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { tx = bitcoin_steal(ctx, peer, idata->btc); if (!tx) - return next_state(ctx, effect, + return next_state(ctx, cstatus, effect, STATE_ERR_INFORMATION_LEAK); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch(ctx, tx, BITCOIN_STEAL_DONE)); /* Expect either close or steal to complete */ - return next_state(ctx, effect, STATE_CLOSE_WAIT_STEAL_CLOSE); + return next_state(ctx, cstatus, effect, + STATE_CLOSE_WAIT_STEAL_CLOSE); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - return next_state(ctx, effect, STATE_ERR_ANCHOR_LOST); + return next_state(ctx, cstatus, effect, STATE_ERR_ANCHOR_LOST); } - return next_state(ctx, effect, STATE_ERR_INTERNAL); + return next_state(ctx, cstatus, effect, STATE_ERR_INTERNAL); old_commit_spotted: /* * bitcoind reported a broadcast of the not-latest commit tx. */ - add_effect(&effect, send_pkt, pkt_err(ctx, "Otherspend noticed")); + add_effect(effect, send_pkt, pkt_err(ctx, "Otherspend noticed")); /* No more packets, no more commands. */ set_peer_cond(peer, PEER_CLOSED); @@ -1151,9 +1169,10 @@ old_commit_spotted: /* If we can't find it, we're lost. */ tx = bitcoin_steal(ctx, peer, idata->btc); if (!tx) - return next_state(ctx, effect, STATE_ERR_INFORMATION_LEAK); - add_effect(&effect, broadcast_tx, tx); - add_effect(&effect, watch, + return next_state(ctx, cstatus, effect, + STATE_ERR_INFORMATION_LEAK); + add_effect(effect, broadcast_tx, tx); + add_effect(effect, watch, bitcoin_watch(ctx, tx, BITCOIN_STEAL_DONE)); - return next_state(ctx, effect, STATE_CLOSE_WAIT_STEAL); + return next_state(ctx, cstatus, effect, STATE_CLOSE_WAIT_STEAL); } diff --git a/state.h b/state.h index f5ce9cdfa..8e8685215 100644 --- a/state.h +++ b/state.h @@ -24,12 +24,6 @@ enum state_effect_type { STATE_EFFECT_send_pkt, STATE_EFFECT_watch, STATE_EFFECT_unwatch, - STATE_EFFECT_cmd_defer, - STATE_EFFECT_cmd_requeue, - STATE_EFFECT_cmd_success, - /* (never applies to CMD_CLOSE) */ - STATE_EFFECT_cmd_fail, - STATE_EFFECT_cmd_close_done, /* FIXME: Use a watch for this?. */ STATE_EFFECT_close_timeout, STATE_EFFECT_htlc_in_progress, @@ -70,17 +64,6 @@ struct state_effect { /* Events to no longer watch for. */ struct watch *unwatch; - /* Defer an input. */ - enum state_input cmd_defer; - - /* Requeue/succeed/fail command. */ - enum state_input cmd_requeue; - enum state_input cmd_success; - void *cmd_fail; - - /* CMD_CLOSE is complete (true if successful mutual close). */ - bool cmd_close_done; - /* Set a timeout for close tx. */ enum state_input close_timeout; @@ -136,11 +119,23 @@ union input { struct htlc_progress *htlc_prog; }; -struct state_effect *state(const tal_t *ctx, - const enum state state, - struct peer *peer, - const enum state_input input, - const union input *idata); +enum command_status { + /* Nothing changed. */ + CMD_NONE, + /* Command succeeded. */ + CMD_SUCCESS, + /* HTLC-command needs re-issuing (theirs takes preference) */ + CMD_REQUEUE, + /* Failed. */ + CMD_FAIL +}; + +enum command_status state(const tal_t *ctx, + const enum state state, + struct peer *peer, + const enum state_input input, + const union input *idata, + struct state_effect **effect); /* Any CMD_SEND_HTLC_* */ #define CMD_SEND_UPDATE_ANY INPUT_MAX diff --git a/test/test_state_coverage.c b/test/test_state_coverage.c index 7b55f5f6c..cb7b7f606 100644 --- a/test/test_state_coverage.c +++ b/test/test_state_coverage.c @@ -104,9 +104,8 @@ struct core_state { uint8_t capped_live_htlcs_to_them; uint8_t capped_live_htlcs_to_us; - bool closing_cmd; bool valid; - bool pad[1]; + bool pad[2]; }; struct peer { @@ -201,7 +200,6 @@ static bool situation_eq(const struct situation *a, const struct situation *b) + sizeof(a->a.s.capped_htlc_spends_to_them) + sizeof(a->a.s.capped_live_htlcs_to_us) + sizeof(a->a.s.capped_live_htlcs_to_them) - + sizeof(a->a.s.closing_cmd) + sizeof(a->a.s.valid) + sizeof(a->a.s.pad))); return structeq(&a->a.s, &b->a.s) && structeq(&a->b.s, &b->b.s); @@ -1188,7 +1186,6 @@ static void peer_init(struct peer *peer, peer->pkt_data[0] = -1; peer->core.current_command = INPUT_NONE; peer->core.event_notifies = 0; - peer->core.closing_cmd = false; peer->name = name; peer->other = other; peer->trail = NULL; @@ -1308,18 +1305,6 @@ static void report_trail(const struct trail *t, const char *problem) exit(1); } -static bool is_current_command(const struct peer *peer, - enum state_input cmd) -{ - if (cmd == CMD_SEND_UPDATE_ANY) { - return is_current_command(peer, CMD_SEND_HTLC_UPDATE) - || is_current_command(peer, CMD_SEND_HTLC_FULFILL) - || is_current_command(peer, CMD_SEND_HTLC_TIMEDOUT) - || is_current_command(peer, CMD_SEND_HTLC_ROUTEFAIL); - } - return peer->core.current_command == cmd; -} - static void add_htlc(struct htlc *to_us, unsigned int *num_to_us, struct htlc *to_them, unsigned int *num_to_them, size_t arrsize, @@ -1475,32 +1460,6 @@ static const char *apply_effects(struct peer *peer, return "unset event unwatched"; peer->core.event_notifies &= ~effect->u.unwatch->events; break; - case STATE_EFFECT_cmd_defer: - /* If it was current command, it is no longer. */ - assert(is_current_command(peer, effect->u.cmd_defer)); - /* We will resubmit this later anyway. */ - peer->core.current_command = INPUT_NONE; - break; - case STATE_EFFECT_cmd_requeue: - assert(is_current_command(peer, effect->u.cmd_requeue)); - peer->core.current_command = INPUT_NONE; - break; - case STATE_EFFECT_cmd_success: - assert(is_current_command(peer, effect->u.cmd_success)); - peer->core.current_command = INPUT_NONE; - break; - case STATE_EFFECT_cmd_fail: - if (peer->core.current_command == INPUT_NONE) - return "Failed command with none current"; - peer->core.current_command = INPUT_NONE; - break; - case STATE_EFFECT_cmd_close_done: - if (!peer->core.closing_cmd) - return tal_fmt(NULL, "%s but not closing", - effect->u.cmd_close_done - ? "Success" : "Failure"); - peer->core.closing_cmd = false; - break; case STATE_EFFECT_close_timeout: add_event(&peer->core.event_notifies, effect->u.close_timeout); @@ -1678,8 +1637,6 @@ static const char *check_changes(const struct peer *old, struct peer *new) input_name(new->core.current_command)); } if (new->cond == PEER_CLOSED) { - if (new->core.closing_cmd) - return "cond CLOSED with pending CMD_CLOSE"; /* FIXME: Move to state core */ /* Can no longer receive packet timeouts, either. */ remove_event_(&new->core.event_notifies, @@ -1690,6 +1647,7 @@ static const char *check_changes(const struct peer *old, struct peer *new) } static const char *apply_all_effects(const struct peer *old, + enum command_status cstatus, struct peer *peer, const struct state_effect *effect, Pkt **output) @@ -1697,6 +1655,17 @@ static const char *apply_all_effects(const struct peer *old, const char *problem; uint64_t effects = 0; *output = NULL; + + if (cstatus != CMD_NONE) { + assert(peer->core.current_command != INPUT_NONE); + /* We should only requeue HTLCs if we're lowprio */ + if (cstatus == CMD_REQUEUE) + assert(!high_priority(old->core.state) + && input_is(peer->core.current_command, + CMD_SEND_UPDATE_ANY)); + peer->core.current_command = INPUT_NONE; + } + problem = apply_effects(peer, effect, &effects, output); if (!problem) problem = check_effects(peer, effect); @@ -1934,6 +1903,7 @@ static void try_input(const struct peer *peer, const char *problem; Pkt *output; const tal_t *ctx = tal(NULL, char); + enum command_status cstatus; copy_peers(©, &other, peer); @@ -1943,7 +1913,7 @@ static void try_input(const struct peer *peer, copy.trail = &t; eliminate_input(&hist->inputs_per_state[copy.core.state], i); - effect = state(ctx, copy.core.state, ©, i, idata); + cstatus = state(ctx, copy.core.state, ©, i, idata, &effect); newstate = get_state_effect(effect, peer->core.state); @@ -1969,7 +1939,7 @@ static void try_input(const struct peer *peer, get_send_pkt(effect)); } - problem = apply_all_effects(peer, ©, effect, &output); + problem = apply_all_effects(peer, cstatus, ©, effect, &output); update_trail(&t, ©, output); if (problem) report_trail(&t, problem); @@ -2002,7 +1972,7 @@ static void try_input(const struct peer *peer, * And if we're being quick, always stop. */ if (quick - || get_effect(effect, STATE_EFFECT_cmd_defer) + || cstatus == CMD_REQUEUE || newstate == STATE_NORMAL_LOWPRIO || newstate == STATE_NORMAL_HIGHPRIO || i == BITCOIN_ANCHOR_OTHERSPEND @@ -2181,10 +2151,8 @@ static void run_peer(const struct peer *peer, if (peer->core.state != STATE_INIT && (peer->cond == PEER_CMD_OK || peer->cond == PEER_BUSY)) { - copy.core.closing_cmd = true; try_input(©, CMD_CLOSE, idata, normalpath, errorpath, prev_trail, hist); - copy.core.closing_cmd = false; } /* Try sending commands if allowed. */ @@ -2334,8 +2302,9 @@ static enum state_input **map_inputs(void) /* This adds to mapping_inputs every input_is() call */ if (!state_is_error(i)) { struct peer dummy; + struct state_effect *effect; memset(&dummy, 0, sizeof(dummy)); - state(ctx, i, &dummy, INPUT_NONE, NULL); + state(ctx, i, &dummy, INPUT_NONE, NULL, &effect); } inps[i] = mapping_inputs; }