diff --git a/state.c b/state.c index 59cd13a4e..60e8ad3a4 100644 --- a/state.c +++ b/state.c @@ -12,93 +12,32 @@ static inline bool high_priority(enum state state) #define toggle_prio(state, name) \ (!high_priority(state) ? name##_HIGHPRIO : name##_LOWPRIO) -#define INIT_EFFECT_broadcast NULL -#define INIT_EFFECT_send NULL -#define INIT_EFFECT_watch NULL -#define INIT_EFFECT_unwatch NULL -#define INIT_EFFECT_defer INPUT_NONE -#define INIT_EFFECT_complete INPUT_NONE -#define INIT_EFFECT_status CMD_STATUS_ONGOING -#define INIT_EFFECT_close_status CMD_STATUS_ONGOING -#define INIT_EFFECT_faildata NULL -#define INIT_EFFECT_stop_packets false -#define INIT_EFFECT_stop_commands false -#define INIT_EFFECT_close_timeout INPUT_NONE -#define INIT_EFFECT_in_error NULL -#define INIT_EFFECT_r_value NULL -#define INIT_EFFECT_watch_htlcs NULL -#define INIT_EFFECT_unwatch_htlc NULL -#define INIT_EFFECT_htlc_in_progress NULL -#define INIT_EFFECT_htlc_abandon false -#define INIT_EFFECT_htlc_fulfill false -#define INIT_EFFECT_update_theirsig NULL -#define INIT_EFFECT_watch_htlc_spend NULL -#define INIT_EFFECT_unwatch_htlc_spend NULL - -void state_effect_init(struct state_effect *effect) -{ - effect->broadcast = INIT_EFFECT_broadcast; - effect->send = INIT_EFFECT_send; - effect->watch = INIT_EFFECT_watch; - effect->unwatch = INIT_EFFECT_unwatch; - effect->defer = INIT_EFFECT_defer; - effect->complete = INIT_EFFECT_complete; - effect->status = INIT_EFFECT_status; - effect->faildata = INIT_EFFECT_faildata; - effect->close_status = INIT_EFFECT_close_status; - effect->stop_packets = INIT_EFFECT_stop_packets; - effect->stop_commands = INIT_EFFECT_stop_commands; - effect->close_timeout = INIT_EFFECT_close_timeout; - effect->in_error = INIT_EFFECT_in_error; - effect->r_value = INIT_EFFECT_r_value; - effect->watch_htlcs = INIT_EFFECT_watch_htlcs; - effect->unwatch_htlc = INIT_EFFECT_unwatch_htlc; - effect->htlc_in_progress = INIT_EFFECT_htlc_in_progress; - effect->htlc_abandon = INIT_EFFECT_htlc_abandon; - effect->htlc_fulfill = INIT_EFFECT_htlc_fulfill; - effect->update_theirsig = INIT_EFFECT_update_theirsig; - effect->watch_htlc_spend = INIT_EFFECT_watch_htlc_spend; - effect->unwatch_htlc_spend = INIT_EFFECT_unwatch_htlc_spend; -} - -#define set_effect(effect, field, val) \ +#define add_effect(e, field, val) \ do { \ - struct state_effect *_e = (effect); \ - assert(_e->field == INIT_EFFECT_##field); \ - _e->field = (val); \ - assert(_e->field != INIT_EFFECT_##field); \ + struct state_effect *_e = tal(ctx, struct state_effect); \ + _e->etype = STATE_EFFECT_##field; \ + _e->u.field = (val); \ + _e->next = (*e); \ + (*e) = _e; \ } while(0) -static void fail_cmd(struct state_effect *effect, - const enum state_input input, - void *faildata) +static struct state_effect *next_state(const tal_t *ctx, + struct state_effect *effect, + const enum state state) { - set_effect(effect, complete, input); - set_effect(effect, status, CMD_STATUS_FAILED); - if (faildata) - set_effect(effect, faildata, faildata); + add_effect(&effect, new_state, state); + return effect; } -static void requeue_cmd(struct state_effect *effect, - const enum state_input input) -{ - set_effect(effect, complete, input); - set_effect(effect, status, CMD_STATUS_REQUEUE); -} - -static void complete_cmd(struct state_effect *effect, - const enum state_input input) -{ - set_effect(effect, complete, input); - set_effect(effect, status, CMD_STATUS_SUCCESS); -} - -enum state state(const enum state state, const struct state_data *sdata, - const enum state_input input, const union input *idata, - struct state_effect *effect) +struct state_effect *state(const tal_t *ctx, + const enum state state, + const struct state_data *sdata, + const enum state_input input, + const union input *idata) { + struct state_effect *effect = NULL; Pkt *decline; - struct bitcoin_tx *steal; + struct bitcoin_tx *tx; Pkt *err; struct htlc_watch *htlcs; struct htlc_progress *htlcprog; @@ -109,26 +48,28 @@ enum state state(const enum state state, const struct state_data *sdata, */ case STATE_INIT_NOANCHOR: assert(input == INPUT_NONE); - set_effect(effect, send, - pkt_open(effect, sdata, + add_effect(&effect, send_pkt, + pkt_open(ctx, sdata, OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR)); - return STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR; + return next_state(ctx, effect, + STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR); case STATE_INIT_WITHANCHOR: assert(input == INPUT_NONE); - set_effect(effect, send, - pkt_open(effect, sdata, + add_effect(&effect, send_pkt, + pkt_open(ctx, sdata, OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR)); - return STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR; + return next_state(ctx, effect, STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR); case STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR: if (input_is(input, PKT_OPEN)) { - err = accept_pkt_open(effect, sdata, idata->pkt); + err = accept_pkt_open(ctx, sdata, idata->pkt, &effect); if (err) goto err_close_nocleanup; - return STATE_OPEN_WAIT_FOR_ANCHOR; + return next_state(ctx, effect, STATE_OPEN_WAIT_FOR_ANCHOR); } else if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* Can't do this until we're open. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_CLOSE)) { goto instant_close; } else if (input_is_pkt(input)) { @@ -137,15 +78,17 @@ enum state state(const enum state state, const struct state_data *sdata, break; case STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR: if (input_is(input, PKT_OPEN)) { - err = accept_pkt_open(effect, sdata, idata->pkt); + err = accept_pkt_open(ctx, sdata, idata->pkt, &effect); if (err) goto err_close_nocleanup; - set_effect(effect, send, pkt_anchor(effect, sdata)); - return STATE_OPEN_WAIT_FOR_COMMIT_SIG; + add_effect(&effect, send_pkt, pkt_anchor(ctx, sdata)); + return next_state(ctx, effect, + STATE_OPEN_WAIT_FOR_COMMIT_SIG); } else if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* Can't do this until we're open. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_CLOSE)) { goto instant_close; } else if (input_is_pkt(input)) { @@ -154,24 +97,26 @@ enum state state(const enum state state, const struct state_data *sdata, break; case STATE_OPEN_WAIT_FOR_ANCHOR: if (input_is(input, PKT_OPEN_ANCHOR)) { - err = accept_pkt_anchor(effect, sdata, idata->pkt); + err = accept_pkt_anchor(ctx, sdata, idata->pkt, &effect); if (err) goto err_close_nocleanup; - set_effect(effect, send, - pkt_open_commit_sig(effect, sdata)); - set_effect(effect, watch, - bitcoin_watch_anchor(effect, sdata, + add_effect(&effect, send_pkt, + pkt_open_commit_sig(ctx, sdata)); + add_effect(&effect, watch, + bitcoin_watch_anchor(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT, BITCOIN_ANCHOR_UNSPENT, BITCOIN_ANCHOR_THEIRSPEND, BITCOIN_ANCHOR_OTHERSPEND)); - return STATE_OPEN_WAITING_THEIRANCHOR; + return next_state(ctx, effect, + STATE_OPEN_WAITING_THEIRANCHOR); } else if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* Can't do this until we're open. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_CLOSE)) { goto instant_close; } else if (input_is_pkt(input)) { @@ -180,24 +125,25 @@ enum state state(const enum state state, const struct state_data *sdata, break; case STATE_OPEN_WAIT_FOR_COMMIT_SIG: if (input_is(input, PKT_OPEN_COMMIT_SIG)) { - err = accept_pkt_open_commit_sig(effect, sdata, - idata->pkt); + err = accept_pkt_open_commit_sig(ctx, sdata, idata->pkt, + &effect); if (err) goto err_start_unilateral_close; - set_effect(effect, broadcast, - bitcoin_anchor(effect, sdata)); - set_effect(effect, watch, - bitcoin_watch_anchor(effect, sdata, + add_effect(&effect, broadcast_tx, + bitcoin_anchor(ctx, sdata)); + add_effect(&effect, watch, + bitcoin_watch_anchor(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE, BITCOIN_ANCHOR_UNSPENT, BITCOIN_ANCHOR_THEIRSPEND, BITCOIN_ANCHOR_OTHERSPEND)); - return STATE_OPEN_WAITING_OURANCHOR; + return next_state(ctx, effect, STATE_OPEN_WAITING_OURANCHOR); } else if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* Can't do this until we're open. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_CLOSE)) { goto instant_close; } else if (input_is_pkt(input)) { @@ -206,47 +152,50 @@ enum state state(const enum state state, const struct state_data *sdata, break; case STATE_OPEN_WAITING_OURANCHOR: if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) { - set_effect(effect, send, - pkt_open_complete(effect, sdata)); - return STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR; + add_effect(&effect, send_pkt, + pkt_open_complete(ctx, sdata)); + return next_state(ctx, effect, + STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { goto anchor_unspent; } else if (input_is(input, PKT_OPEN_COMPLETE)) { /* Ignore until we've hit depth ourselves. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* Can't do this until we're open. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { /* This should be impossible. */ - return STATE_ERR_INFORMATION_LEAK; + return next_state(ctx, effect, STATE_ERR_INFORMATION_LEAK); } else if (input_is(input, CMD_CLOSE)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); goto start_closing; } else if (input_is(input, PKT_CLOSE)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); goto accept_closing; } else if (input_is_pkt(input)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE)); goto unexpected_pkt; @@ -255,51 +204,53 @@ enum state state(const enum state state, const struct state_data *sdata, case STATE_OPEN_WAITING_THEIRANCHOR: if (input_is(input, BITCOIN_ANCHOR_TIMEOUT)) { /* Anchor didn't reach blockchain in reasonable time. */ - set_effect(effect, send, - pkt_err(effect, "Anchor timed out")); - return STATE_ERR_ANCHOR_TIMEOUT; + add_effect(&effect, send_pkt, + pkt_err(ctx, "Anchor timed out")); + return next_state(ctx, effect, STATE_ERR_ANCHOR_TIMEOUT); } else if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) { - set_effect(effect, send, - pkt_open_complete(effect, sdata)); - return STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR; + add_effect(&effect, send_pkt, + pkt_open_complete(ctx, sdata)); + return next_state(ctx, effect, STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { /* This should be impossible. */ - return STATE_ERR_INFORMATION_LEAK; + return next_state(ctx, effect, STATE_ERR_INFORMATION_LEAK); } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); goto them_unilateral; } else if (input_is(input, PKT_OPEN_COMPLETE)) { /* Ignore until we've hit depth ourselves. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* Can't do this until we're open. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_CLOSE)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); goto start_closing; } else if (input_is(input, PKT_CLOSE)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); goto accept_closing; } else if (input_is_pkt(input)) { /* We no longer care about anchor depth. */ - set_effect(effect, unwatch, - bitcoin_unwatch_anchor_depth(effect, sdata, + add_effect(&effect, unwatch, + bitcoin_unwatch_anchor_depth(ctx, sdata, BITCOIN_ANCHOR_DEPTHOK, BITCOIN_ANCHOR_TIMEOUT)); goto unexpected_pkt; @@ -310,24 +261,25 @@ enum state state(const enum state state, const struct state_data *sdata, if (input_is(input, PKT_OPEN_COMPLETE)) { /* Ready for business! Anchorer goes first. */ if (state == STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR) - return STATE_NORMAL_HIGHPRIO; + return next_state(ctx, effect, STATE_NORMAL_HIGHPRIO); else - return STATE_NORMAL_LOWPRIO; + return next_state(ctx, effect, STATE_NORMAL_LOWPRIO); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { goto anchor_unspent; /* Nobody should be able to spend anchor, except via the * commit txs. */ } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - return STATE_ERR_INFORMATION_LEAK; + return next_state(ctx, effect, STATE_ERR_INFORMATION_LEAK); } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { goto them_unilateral; } else if (input_is(input, PKT_OPEN_COMPLETE)) { /* Ready for business! */ - return STATE_NORMAL_HIGHPRIO; + return next_state(ctx, effect, STATE_NORMAL_HIGHPRIO); } else if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* Can't do this until we're open. */ - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + /* No state change. */ + return effect; } else if (input_is(input, CMD_CLOSE)) { goto start_closing; } else if (input_is(input, PKT_CLOSE)) { @@ -344,35 +296,39 @@ enum state state(const enum state state, const struct state_data *sdata, case STATE_NORMAL_HIGHPRIO: if (input_is(input, CMD_SEND_HTLC_UPDATE)) { /* We are to send an HTLC update. */ - set_effect(effect, send, - pkt_htlc_update(effect, sdata, + add_effect(&effect, send_pkt, + pkt_htlc_update(ctx, sdata, idata->htlc_prog)); - set_effect(effect, htlc_in_progress, idata->htlc_prog); - return prio(state, STATE_WAIT_FOR_HTLC_ACCEPT); + add_effect(&effect, htlc_in_progress, idata->htlc_prog); + return next_state(ctx, 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!) */ - set_effect(effect, r_value, - r_value_from_cmd(effect, sdata, idata->htlc)); - set_effect(effect, send, - pkt_htlc_fulfill(effect, sdata, + add_effect(&effect, r_value, + r_value_from_cmd(ctx, sdata, idata->htlc)); + add_effect(&effect, send_pkt, + pkt_htlc_fulfill(ctx, sdata, idata->htlc_prog)); - set_effect(effect, htlc_in_progress, idata->htlc_prog); - return prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT); + add_effect(&effect, htlc_in_progress, idata->htlc_prog); + return next_state(ctx, effect, + prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT)); } else if (input_is(input, CMD_SEND_HTLC_TIMEDOUT)) { /* We are to send an HTLC timedout. */ - set_effect(effect, send, - pkt_htlc_timedout(effect, sdata, + add_effect(&effect, send_pkt, + pkt_htlc_timedout(ctx, sdata, idata->htlc_prog)); - set_effect(effect, htlc_in_progress, idata->htlc_prog); - return prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT); + add_effect(&effect, htlc_in_progress, idata->htlc_prog); + return next_state(ctx, effect, + prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT)); } else if (input_is(input, CMD_SEND_HTLC_ROUTEFAIL)) { /* We are to send an HTLC routefail. */ - set_effect(effect, send, - pkt_htlc_routefail(effect, sdata, + add_effect(&effect, send_pkt, + pkt_htlc_routefail(ctx, sdata, idata->htlc_prog)); - set_effect(effect, htlc_in_progress, idata->htlc_prog); - return prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT); + add_effect(&effect, htlc_in_progress, idata->htlc_prog); + return next_state(ctx, effect, + prio(state, STATE_WAIT_FOR_UPDATE_ACCEPT)); } else if (input_is(input, CMD_CLOSE)) { goto start_closing; } else if (input_is(input, PKT_UPDATE_ADD_HTLC)) { @@ -399,14 +355,15 @@ enum state state(const enum state state, const struct state_data *sdata, case STATE_WAIT_FOR_HTLC_ACCEPT_HIGHPRIO: /* HTLCs can also evoke a refusal. */ if (input_is(input, PKT_UPDATE_DECLINE_HTLC)) { - fail_cmd(effect, CMD_SEND_HTLC_UPDATE, idata->pkt); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, idata->pkt); + add_effect(&effect, htlc_abandon, true); /* No update means no priority change. */ - return prio(state, STATE_NORMAL); + return next_state(ctx, effect, + prio(state, STATE_NORMAL)); /* They can't close with an HTLC, so only possible here */ } else if (input_is(input, PKT_CLOSE)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, NULL); + add_effect(&effect, htlc_abandon, true); goto accept_closing; } /* Fall thru */ @@ -415,104 +372,106 @@ enum state state(const enum state state, const struct state_data *sdata, if (input_is(input, PKT_UPDATE_ADD_HTLC)) { /* If we're high priority, ignore their packet */ if (high_priority(state)) - return state; + return effect; /* Otherwise, process their request first: defer ours */ - requeue_cmd(effect, CMD_SEND_UPDATE_ANY); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); + add_effect(&effect, htlc_abandon, true); 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 state; + return effect; /* Otherwise, process their request first: defer ours */ - requeue_cmd(effect, CMD_SEND_UPDATE_ANY); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); + add_effect(&effect, htlc_abandon, true); 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 state; + return effect; /* Otherwise, process their request first: defer ours */ - requeue_cmd(effect, CMD_SEND_UPDATE_ANY); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); + add_effect(&effect, htlc_abandon, true); 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 state; + return effect; /* Otherwise, process their request first: defer ours */ - requeue_cmd(effect, CMD_SEND_UPDATE_ANY); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_requeue, CMD_SEND_UPDATE_ANY); + add_effect(&effect, htlc_abandon, true); goto accept_htlc_routefail; } else if (input_is(input, PKT_UPDATE_ACCEPT)) { struct signature *sig; - err = accept_pkt_update_accept(effect, sdata, - idata->pkt, &sig); + err = accept_pkt_update_accept(ctx, sdata, idata->pkt, + &sig, &effect); if (err) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, NULL); + add_effect(&effect, htlc_abandon, true); goto err_start_unilateral_close; } - set_effect(effect, update_theirsig, sig); - set_effect(effect, send, - pkt_update_signature(effect, sdata)); + add_effect(&effect, update_theirsig, sig); + add_effect(&effect, send_pkt, + pkt_update_signature(ctx, sdata)); /* HTLC is signed (though old tx not revoked yet!) */ - set_effect(effect, htlc_fulfill, true); - return prio(state, STATE_WAIT_FOR_UPDATE_COMPLETE); + add_effect(&effect, htlc_fulfill, true); + return next_state(ctx, effect, + prio(state, STATE_WAIT_FOR_UPDATE_COMPLETE)); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, NULL); + add_effect(&effect, htlc_abandon, true); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, NULL); + add_effect(&effect, htlc_abandon, true); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, NULL); + add_effect(&effect, htlc_abandon, true); goto old_commit_spotted; } else if (input_is(input, CMD_CLOSE)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, NULL); + add_effect(&effect, htlc_abandon, true); goto start_closing; } else if (input_is_pkt(input)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); - set_effect(effect, htlc_abandon, true); + add_effect(&effect, cmd_fail, NULL); + add_effect(&effect, htlc_abandon, true); goto unexpected_pkt; } break; case STATE_WAIT_FOR_UPDATE_COMPLETE_LOWPRIO: case STATE_WAIT_FOR_UPDATE_COMPLETE_HIGHPRIO: if (input_is(input, PKT_UPDATE_COMPLETE)) { - err = accept_pkt_update_complete(effect, sdata, - idata->pkt); + err = accept_pkt_update_complete(ctx, sdata, idata->pkt, + &effect); if (err) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); + add_effect(&effect, cmd_fail, NULL); goto err_start_unilateral_close; } - complete_cmd(effect, CMD_SEND_UPDATE_ANY); - return toggle_prio(state, STATE_NORMAL); + add_effect(&effect, cmd_success, CMD_SEND_UPDATE_ANY); + return next_state(ctx, effect, + toggle_prio(state, STATE_NORMAL)); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); + add_effect(&effect, cmd_fail, NULL); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); + add_effect(&effect, cmd_fail, NULL); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); + add_effect(&effect, cmd_fail, NULL); goto old_commit_spotted; } else if (input_is(input, PKT_CLOSE)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); + add_effect(&effect, cmd_fail, NULL); goto accept_closing; } else if (input_is(input, CMD_CLOSE)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); + add_effect(&effect, cmd_fail, NULL); goto start_closing; } else if (input_is_pkt(input)) { - fail_cmd(effect, CMD_SEND_UPDATE_ANY, NULL); + add_effect(&effect, cmd_fail, NULL); goto unexpected_pkt; } break; @@ -520,107 +479,112 @@ enum state state(const enum state state, const struct state_data *sdata, case STATE_WAIT_FOR_UPDATE_SIG_HIGHPRIO: if (input_is(input, PKT_UPDATE_SIGNATURE)) { struct signature *sig; - err = accept_pkt_update_signature(effect, sdata, - idata->pkt, &sig); + err = accept_pkt_update_signature(ctx, sdata, idata->pkt, + &sig, &effect); if (err) { - set_effect(effect, htlc_abandon, true); + add_effect(&effect, htlc_abandon, true); goto err_start_unilateral_close; } - set_effect(effect, update_theirsig, sig); - set_effect(effect, send, - pkt_update_complete(effect, sdata)); - set_effect(effect, htlc_fulfill, true); + add_effect(&effect, update_theirsig, sig); + add_effect(&effect, send_pkt, + pkt_update_complete(ctx, sdata)); + add_effect(&effect, htlc_fulfill, true); /* Toggle between high and low priority states. */ - return toggle_prio(state, STATE_NORMAL); + return next_state(ctx, effect, + toggle_prio(state, STATE_NORMAL)); } else if (input_is(input, CMD_SEND_UPDATE_ANY)) { - set_effect(effect, defer, input); - return state; + add_effect(&effect, cmd_defer, input); + return effect; } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - set_effect(effect, htlc_abandon, true); + add_effect(&effect, htlc_abandon, true); goto anchor_unspent; } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - set_effect(effect, htlc_abandon, true); + add_effect(&effect, htlc_abandon, true); goto them_unilateral; } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - set_effect(effect, htlc_abandon, true); + add_effect(&effect, htlc_abandon, true); goto old_commit_spotted; } else if (input_is(input, CMD_CLOSE)) { - set_effect(effect, htlc_abandon, true); + add_effect(&effect, htlc_abandon, true); goto start_closing; } else if (input_is(input, PKT_CLOSE)) { - set_effect(effect, htlc_abandon, true); + add_effect(&effect, htlc_abandon, true); goto accept_closing; } else if (input_is_pkt(input)) { - set_effect(effect, htlc_abandon, true); + add_effect(&effect, htlc_abandon, true); goto unexpected_pkt; } break; case STATE_WAIT_FOR_CLOSE_COMPLETE: if (input_is(input, PKT_CLOSE_COMPLETE)) { - err = accept_pkt_close_complete(effect, sdata, - idata->pkt); + err = accept_pkt_close_complete(ctx, sdata, idata->pkt, + &effect); if (err) goto err_start_unilateral_close_already_closing; - set_effect(effect, close_status, CMD_STATUS_SUCCESS); - set_effect(effect, send, pkt_close_ack(effect, sdata)); - set_effect(effect, broadcast, - bitcoin_close(effect, sdata)); - set_effect(effect, stop_commands, true); - set_effect(effect, stop_packets, true); - return STATE_CLOSE_WAIT_CLOSE; + add_effect(&effect, cmd_close_done, true); + add_effect(&effect, send_pkt, + pkt_close_ack(ctx, sdata)); + add_effect(&effect, broadcast_tx, + bitcoin_close(ctx, sdata)); + add_effect(&effect, stop_commands, true); + add_effect(&effect, stop_packets, true); + return next_state(ctx, 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(effect, sdata, - idata->pkt); + err = accept_pkt_simultaneous_close(ctx, sdata, + idata->pkt, + &effect); if (err) goto err_start_unilateral_close_already_closing; - set_effect(effect, close_status, CMD_STATUS_SUCCESS); - set_effect(effect, send, pkt_close_ack(effect, sdata)); - set_effect(effect, broadcast, - bitcoin_close(effect, sdata)); - set_effect(effect, stop_commands, true); - set_effect(effect, stop_packets, true); - return STATE_CLOSE_WAIT_CLOSE; + add_effect(&effect, cmd_close_done, true); + add_effect(&effect, send_pkt, + pkt_close_ack(ctx, sdata)); + add_effect(&effect, broadcast_tx, + bitcoin_close(ctx, sdata)); + add_effect(&effect, stop_commands, true); + add_effect(&effect, stop_packets, true); + return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE); } else if (input_is(input, PKT_ERROR)) { - set_effect(effect, in_error, - set_errpkt(effect, idata->pkt)); + add_effect(&effect, in_error, + set_errpkt(ctx, idata->pkt)); goto start_unilateral_close_already_closing; } else if (input_is_pkt(input)) { /* We ignore all other packets while closing. */ - return STATE_WAIT_FOR_CLOSE_COMPLETE; + return next_state(ctx, 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(effect, "Close timed out"); + err = pkt_err(ctx, "Close timed out"); goto err_start_unilateral_close_already_closing; } - set_effect(effect, close_status, CMD_STATUS_FAILED); - set_effect(effect, stop_commands, true); + add_effect(&effect, cmd_close_done, false); + add_effect(&effect, stop_commands, true); goto fail_during_close; case STATE_WAIT_FOR_CLOSE_ACK: if (input_is(input, PKT_CLOSE_ACK)) { - err = accept_pkt_close_ack(effect, sdata, idata->pkt); + err = accept_pkt_close_ack(ctx, sdata, idata->pkt, + &effect); if (err) - set_effect(effect, send, err); - set_effect(effect, stop_packets, true); + add_effect(&effect, send_pkt, err); + add_effect(&effect, stop_packets, true); /* Just wait for close to happen now. */ - return STATE_CLOSE_WAIT_CLOSE; + return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE); } else if (input_is_pkt(input)) { if (input_is(input, PKT_ERROR)) { - set_effect(effect, in_error, - tal_steal(effect, idata->pkt)); + add_effect(&effect, in_error, + tal_steal(ctx, idata->pkt)); } else { - set_effect(effect, send, - unexpected_pkt(effect, input)); + add_effect(&effect, send_pkt, + unexpected_pkt(ctx, input)); } - set_effect(effect, stop_packets, true); + add_effect(&effect, stop_packets, true); /* Just wait for close to happen now. */ - return STATE_CLOSE_WAIT_CLOSE; + return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE); } else if (input_is(input, BITCOIN_CLOSE_DONE)) { /* They didn't ack, but we're closed, so stop. */ - set_effect(effect, stop_packets, true); - return STATE_CLOSED; + add_effect(&effect, stop_packets, true); + return next_state(ctx, effect, STATE_CLOSED); } goto fail_during_close; @@ -683,45 +647,45 @@ enum state state(const enum state state, const struct state_data *sdata, /* One a steal is complete, we don't care about htlcs * (we stole them all) */ if (bits & STATE_CLOSE_HTLCS_BIT) - set_effect(effect, unwatch_htlc, - htlc_unwatch_all(effect, sdata)); - return STATE_CLOSED; + add_effect(&effect, unwatch_htlc, + htlc_unwatch_all(ctx, sdata)); + return next_state(ctx, effect, STATE_CLOSED); } if ((bits & STATE_CLOSE_SPENDTHEM_BIT) && input_is(input, BITCOIN_SPEND_THEIRS_DONE)) { BUILD_ASSERT(!((STATE_CLOSE_WAIT_HTLCS - base) & STATE_CLOSE_SPENDTHEM_BIT)); - return closed; + return next_state(ctx, effect, closed); } if ((bits & STATE_CLOSE_CLOSE_BIT) && input_is(input, BITCOIN_CLOSE_DONE)) { BUILD_ASSERT(!((STATE_CLOSE_WAIT_HTLCS - base) & STATE_CLOSE_CLOSE_BIT)); - return closed; + return next_state(ctx, effect, closed); } if ((bits & STATE_CLOSE_OURCOMMIT_BIT) && input_is(input, BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED)) { BUILD_ASSERT(!((STATE_CLOSE_WAIT_HTLCS - base) & STATE_CLOSE_OURCOMMIT_BIT)); + tx = bitcoin_spend_ours(ctx, sdata); /* Now we need to wait for our commit to be done. */ - set_effect(effect, broadcast, - bitcoin_spend_ours(effect, sdata)); - set_effect(effect, watch, - bitcoin_watch(effect, effect->broadcast, + 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 base + bits; + return next_state(ctx, effect, base + bits); } if ((bits & STATE_CLOSE_SPENDOURS_BIT) && input_is(input, BITCOIN_SPEND_OURS_DONE)) { BUILD_ASSERT(!((STATE_CLOSE_WAIT_HTLCS - base) & STATE_CLOSE_SPENDOURS_BIT)); - return closed; + return next_state(ctx, effect, closed); } /* If we have htlcs, we can get other inputs... */ @@ -732,81 +696,82 @@ enum state state(const enum state state, const struct state_data *sdata, & ~STATE_CLOSE_HTLCS_BIT) == STATE_CLOSED); bits &= ~STATE_CLOSE_HTLCS_BIT; - return base + bits; + return next_state(ctx, effect, base + bits); } else if (input_is(input, BITCOIN_HTLC_TOTHEM_SPENT)) { /* They revealed R value. */ - set_effect(effect, r_value, - bitcoin_r_value(effect, idata->htlc)); + add_effect(&effect, r_value, + bitcoin_r_value(ctx, idata->htlc)); /* We don't care any more. */ - set_effect(effect, unwatch_htlc, - htlc_unwatch(effect, idata->htlc, + add_effect(&effect, unwatch_htlc, + htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return state; + return effect; } else if (input_is(input, BITCOIN_HTLC_TOTHEM_TIMEOUT)){ + tx = bitcoin_htlc_timeout(ctx, + sdata, + idata->htlc); /* HTLC timed out, spend it back to us. */ - set_effect(effect, broadcast, - bitcoin_htlc_timeout(effect, - sdata, - idata->htlc)); + 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. */ - set_effect(effect, watch_htlc_spend, - htlc_spend_watch(effect, - effect->broadcast, + add_effect(&effect, watch_htlc_spend, + htlc_spend_watch(ctx, + tx, idata->cmd, BITCOIN_HTLC_RETURN_SPEND_DONE)); - return state; + return effect; } else if (input_is(input, INPUT_RVALUE)) { + tx = bitcoin_htlc_spend(ctx, sdata, + idata->htlc); + /* This gives us the r value. */ - set_effect(effect, r_value, - r_value_from_cmd(effect, sdata, + add_effect(&effect, r_value, + r_value_from_cmd(ctx, sdata, idata->htlc)); /* Spend it... */ - set_effect(effect, broadcast, - bitcoin_htlc_spend(effect, sdata, - idata->htlc)); + add_effect(&effect, broadcast_tx, tx); /* We're done when it gets buried. */ - set_effect(effect, watch_htlc_spend, - htlc_spend_watch(effect, - effect->broadcast, + 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. */ - set_effect(effect, unwatch_htlc, - htlc_unwatch(effect, idata->htlc, + add_effect(&effect, unwatch_htlc, + htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return state; + return effect; } else if (input_is(input, BITCOIN_HTLC_FULFILL_SPEND_DONE)) { /* Stop watching spend, send * INPUT_NO_MORE_HTLCS when done. */ - set_effect(effect, unwatch_htlc_spend, - htlc_spend_unwatch(effect, + add_effect(&effect, unwatch_htlc_spend, + htlc_spend_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return state; + return effect; } else if (input_is(input, BITCOIN_HTLC_RETURN_SPEND_DONE)) { /* Stop watching spend, send * INPUT_NO_MORE_HTLCS when done. */ - set_effect(effect, unwatch_htlc_spend, - htlc_spend_unwatch(effect, + 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. */ - set_effect(effect, unwatch_htlc, - htlc_unwatch(effect, idata->htlc, + add_effect(&effect, unwatch_htlc, + htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return state; + return effect; } else if (input_is(input, BITCOIN_HTLC_TOUS_TIMEOUT)) { /* They can spend, we no longer care * about this HTLC. */ - set_effect(effect, unwatch_htlc, - htlc_unwatch(effect, idata->htlc, + add_effect(&effect, unwatch_htlc, + htlc_unwatch(ctx, idata->htlc, INPUT_NO_MORE_HTLCS)); - return state; + return effect; } } @@ -819,36 +784,35 @@ enum state state(const enum state state, const struct state_data *sdata, * (even if they already have, due to tx malleability). */ if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { - set_effect(effect, broadcast, - bitcoin_spend_theirs(effect, sdata, - idata->btc)); - set_effect(effect, watch, - bitcoin_watch(effect, effect->broadcast, + tx = bitcoin_spend_theirs(ctx, sdata, idata->btc); + add_effect(&effect, broadcast_tx, tx); + add_effect(&effect, watch, + bitcoin_watch(ctx, tx, BITCOIN_SPEND_THEIRS_DONE)); /* HTLC watches. */ - htlcs = htlc_outputs_their_commit(effect, sdata, + htlcs = htlc_outputs_their_commit(ctx, sdata, idata->btc, BITCOIN_HTLC_TOUS_TIMEOUT, BITCOIN_HTLC_TOTHEM_SPENT, BITCOIN_HTLC_TOTHEM_TIMEOUT); if (htlcs) { - set_effect(effect, watch_htlcs, htlcs); + add_effect(&effect, watch_htlcs, htlcs); bits |= STATE_CLOSE_HTLCS_BIT; } bits |= STATE_CLOSE_SPENDTHEM_BIT; - return base + bits; + return next_state(ctx, effect, base + bits); /* This can happen multiple times: need to steal ALL */ } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - struct bitcoin_tx *steal; - steal = bitcoin_steal(effect, sdata, idata->btc); - if (!steal) - return STATE_ERR_INFORMATION_LEAK; - set_effect(effect, broadcast, steal); - set_effect(effect, watch, - bitcoin_watch(effect, effect->broadcast, + tx = bitcoin_steal(ctx, sdata, idata->btc); + if (!tx) + return next_state(ctx, effect, + STATE_ERR_INFORMATION_LEAK); + add_effect(&effect, broadcast_tx, tx); + add_effect(&effect, watch, + bitcoin_watch(ctx, tx, BITCOIN_STEAL_DONE)); bits |= STATE_CLOSE_STEAL_BIT; - return base + bits; + return next_state(ctx, effect, base + bits); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) goto anchor_unspent; @@ -869,11 +833,11 @@ enum state state(const enum state state, const struct state_data *sdata, 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 STATE_ERR_INTERNAL; + return next_state(ctx, effect, STATE_ERR_INTERNAL); } /* State machine should handle all possible states. */ - return STATE_ERR_INTERNAL; + return next_state(ctx, effect, STATE_ERR_INTERNAL); unexpected_pkt: /* @@ -881,17 +845,17 @@ unexpected_pkt: */ /* Don't reply to an error with an error. */ if (input_is(input, PKT_ERROR)) { - set_effect(effect, in_error, set_errpkt(effect, idata->pkt)); + add_effect(&effect, in_error, set_errpkt(ctx, idata->pkt)); goto start_unilateral_close; } - err = unexpected_pkt(effect, input); + err = unexpected_pkt(ctx, input); goto err_start_unilateral_close; unexpected_pkt_nocleanup: /* * Unexpected packet, but nothing sent to chain yet, so no cleanup. */ - err = unexpected_pkt(effect, input); + err = unexpected_pkt(ctx, input); goto err_close_nocleanup; anchor_unspent: @@ -900,72 +864,75 @@ anchor_unspent: * then we're malfunctioning. If they double-spent it, then they * managed to cheat us: post_to_reddit(); */ - return STATE_ERR_ANCHOR_LOST; + return next_state(ctx, 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. */ - set_effect(effect, send, err); - set_effect(effect, stop_packets, true); - set_effect(effect, stop_commands, true); - return STATE_CLOSED; + add_effect(&effect, send_pkt, err); + add_effect(&effect, stop_packets, true); + add_effect(&effect, stop_commands, true); + return next_state(ctx, effect, STATE_CLOSED); err_start_unilateral_close: /* * They timed out, or were broken; we are going to close unilaterally. */ - set_effect(effect, send, err); + add_effect(&effect, send_pkt, err); start_unilateral_close: /* * Close unilaterally. */ /* No more inputs, no more commands. */ - set_effect(effect, stop_packets, true); - set_effect(effect, stop_commands, true); - set_effect(effect, broadcast, bitcoin_commit(effect, sdata)); - set_effect(effect, watch, - bitcoin_watch_delayed(effect, effect->broadcast, + add_effect(&effect, stop_packets, true); + add_effect(&effect, stop_commands, true); + tx = bitcoin_commit(ctx, sdata); + add_effect(&effect, broadcast_tx, tx); + add_effect(&effect, watch, + bitcoin_watch_delayed(ctx, tx, BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED)); /* HTLC watches. */ - htlcs = htlc_outputs_our_commit(effect, sdata, effect->broadcast, + htlcs = htlc_outputs_our_commit(ctx, sdata, tx, BITCOIN_HTLC_TOUS_TIMEOUT, BITCOIN_HTLC_TOTHEM_SPENT, BITCOIN_HTLC_TOTHEM_TIMEOUT); if (htlcs) { - set_effect(effect, watch_htlcs, htlcs); - return STATE_CLOSE_WAIT_OURCOMMIT_WITH_HTLCS; + add_effect(&effect, watch_htlcs, htlcs); + return next_state(ctx, effect, + STATE_CLOSE_WAIT_OURCOMMIT_WITH_HTLCS); } - return STATE_CLOSE_WAIT_OURCOMMIT; + return next_state(ctx, effect, STATE_CLOSE_WAIT_OURCOMMIT); err_start_unilateral_close_already_closing: /* * They timed out, or were broken; we are going to close unilaterally. */ - set_effect(effect, send, err); + add_effect(&effect, send_pkt, err); start_unilateral_close_already_closing: - set_effect(effect, close_status, CMD_STATUS_FAILED); + add_effect(&effect, cmd_close_done, false); /* * Close unilaterally. */ /* No more inputs, no more commands. */ - set_effect(effect, stop_packets, true); - set_effect(effect, stop_commands, true); - set_effect(effect, broadcast, bitcoin_commit(effect, sdata)); - set_effect(effect, watch, - bitcoin_watch_delayed(effect, effect->broadcast, + add_effect(&effect, stop_packets, true); + add_effect(&effect, stop_commands, true); + tx = bitcoin_commit(ctx, sdata); + 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(sdata)) - return STATE_ERR_INTERNAL; + return next_state(ctx, effect, STATE_ERR_INTERNAL); - return STATE_CLOSE_WAIT_CLOSE_OURCOMMIT; + return next_state(ctx, effect, STATE_CLOSE_WAIT_CLOSE_OURCOMMIT); them_unilateral: assert(input == BITCOIN_ANCHOR_THEIRSPEND); @@ -973,67 +940,70 @@ them_unilateral: /* * Bitcoind tells us they did unilateral close. */ - set_effect(effect, send, pkt_err(effect, "Commit tx noticed")); + add_effect(&effect, send_pkt, pkt_err(ctx, "Commit tx noticed")); /* No more inputs, no more commands. */ - set_effect(effect, stop_packets, true); - set_effect(effect, stop_commands, true); - set_effect(effect, broadcast, - bitcoin_spend_theirs(effect, sdata, idata->btc)); - set_effect(effect, watch, - bitcoin_watch(effect, effect->broadcast, + add_effect(&effect, stop_packets, true); + add_effect(&effect, stop_commands, true); + tx = bitcoin_spend_theirs(ctx, sdata, idata->btc); + add_effect(&effect, broadcast_tx, tx); + add_effect(&effect, watch, + bitcoin_watch(ctx, tx, BITCOIN_SPEND_THEIRS_DONE)); /* HTLC watches (based on what they broadcast, which *may* be out * of step with our current state by +/- 1 htlc. */ - htlcs = htlc_outputs_their_commit(effect, sdata, idata->btc, + htlcs = htlc_outputs_their_commit(ctx, sdata, idata->btc, BITCOIN_HTLC_TOUS_TIMEOUT, BITCOIN_HTLC_TOTHEM_SPENT, BITCOIN_HTLC_TOTHEM_TIMEOUT); if (htlcs) { - set_effect(effect, watch_htlcs, htlcs); - return STATE_CLOSE_WAIT_SPENDTHEM_WITH_HTLCS; + add_effect(&effect, watch_htlcs, htlcs); + return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM_WITH_HTLCS); } - return STATE_CLOSE_WAIT_SPENDTHEM; + return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM); accept_htlc_update: - err = accept_pkt_htlc_update(effect, sdata, idata->pkt, &decline, - &htlcprog); + err = accept_pkt_htlc_update(ctx, sdata, idata->pkt, &decline, &htlcprog, + &effect); if (err) goto err_start_unilateral_close; if (decline) { - set_effect(effect, send, decline); + add_effect(&effect, send_pkt, decline); /* No update means no priority change. */ - return prio(state, STATE_NORMAL); + return next_state(ctx, effect, prio(state, STATE_NORMAL)); } - set_effect(effect, htlc_in_progress, htlcprog); - set_effect(effect, send, pkt_update_accept(effect, sdata)); - return prio(state, STATE_WAIT_FOR_UPDATE_SIG); + add_effect(&effect, htlc_in_progress, htlcprog); + add_effect(&effect, send_pkt, pkt_update_accept(ctx, sdata)); + return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); accept_htlc_routefail: - err = accept_pkt_htlc_routefail(effect, sdata, idata->pkt, &htlcprog); + err = accept_pkt_htlc_routefail(ctx, sdata, idata->pkt, &htlcprog, + &effect); if (err) goto err_start_unilateral_close; - set_effect(effect, htlc_in_progress, htlcprog); - set_effect(effect, send, pkt_update_accept(effect, sdata)); - return prio(state, STATE_WAIT_FOR_UPDATE_SIG); + add_effect(&effect, htlc_in_progress, htlcprog); + add_effect(&effect, send_pkt, pkt_update_accept(ctx, sdata)); + return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); accept_htlc_timedout: - err = accept_pkt_htlc_timedout(effect, sdata, idata->pkt, &htlcprog); + err = accept_pkt_htlc_timedout(ctx, sdata, idata->pkt, &htlcprog, + &effect); if (err) goto err_start_unilateral_close; - set_effect(effect, htlc_in_progress, htlcprog); - set_effect(effect, send, pkt_update_accept(effect, sdata)); - return prio(state, STATE_WAIT_FOR_UPDATE_SIG); + add_effect(&effect, htlc_in_progress, htlcprog); + add_effect(&effect, send_pkt, pkt_update_accept(ctx, sdata)); + return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); accept_htlc_fulfill: - err = accept_pkt_htlc_fulfill(effect, sdata, idata->pkt, &htlcprog); + err = accept_pkt_htlc_fulfill(ctx, sdata, idata->pkt, &htlcprog, + &effect); if (err) goto err_start_unilateral_close; - set_effect(effect, htlc_in_progress, htlcprog); - set_effect(effect, send, pkt_update_accept(effect, sdata)); - set_effect(effect, r_value, r_value_from_pkt(effect, idata->pkt)); - return prio(state, STATE_WAIT_FOR_UPDATE_SIG); + add_effect(&effect, htlc_in_progress, htlcprog); + add_effect(&effect, send_pkt, pkt_update_accept(ctx, sdata)); + add_effect(&effect, r_value, r_value_from_pkt(ctx, idata->pkt)); + return next_state(ctx, effect, prio(state, STATE_WAIT_FOR_UPDATE_SIG)); start_closing: /* @@ -1041,64 +1011,64 @@ start_closing: */ /* Protocol doesn't (currently?) allow closing with HTLCs. */ if (committed_to_htlcs(sdata)) { - set_effect(effect, close_status, CMD_STATUS_FAILED); - err = pkt_err(effect, "Close forced due to HTLCs"); + add_effect(&effect, cmd_close_done, false); + err = pkt_err(ctx, "Close forced due to HTLCs"); goto err_start_unilateral_close; } - set_effect(effect, close_timeout, INPUT_CLOSE_COMPLETE_TIMEOUT); + add_effect(&effect, close_timeout, INPUT_CLOSE_COMPLETE_TIMEOUT); - set_effect(effect, watch, - bitcoin_watch_close(effect, sdata, BITCOIN_CLOSE_DONE)); + add_effect(&effect, watch, + bitcoin_watch_close(ctx, sdata, BITCOIN_CLOSE_DONE)); /* As soon as we send packet, they could close. */ - set_effect(effect, send, pkt_close(effect, sdata)); - return STATE_WAIT_FOR_CLOSE_COMPLETE; + add_effect(&effect, send_pkt, pkt_close(ctx, sdata)); + return next_state(ctx, effect, STATE_WAIT_FOR_CLOSE_COMPLETE); accept_closing: - err = accept_pkt_close(effect, sdata, idata->pkt); + err = accept_pkt_close(ctx, sdata, idata->pkt, &effect); if (err) goto err_start_unilateral_close; /* As soon as we send packet, they could close. */ - set_effect(effect, watch, - bitcoin_watch_close(effect, sdata, BITCOIN_CLOSE_DONE)); - set_effect(effect, send, pkt_close_complete(effect, sdata)); + add_effect(&effect, watch, + bitcoin_watch_close(ctx, sdata, BITCOIN_CLOSE_DONE)); + add_effect(&effect, send_pkt, pkt_close_complete(ctx, sdata)); /* No more commands, we're already closing. */ - set_effect(effect, stop_commands, true); - return STATE_WAIT_FOR_CLOSE_ACK; + add_effect(&effect, stop_commands, true); + return next_state(ctx, 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. */ - set_effect(effect, close_status, CMD_STATUS_SUCCESS); + add_effect(&effect, cmd_close_done, true); /* FIXME: Should we tell other side we're going? */ - set_effect(effect, stop_packets, true); - set_effect(effect, stop_commands, true); + add_effect(&effect, stop_packets, true); + add_effect(&effect, stop_commands, true); /* We can't have any HTLCs, since we haven't started. */ if (committed_to_htlcs(sdata)) - return STATE_ERR_INTERNAL; - return STATE_CLOSED; + return next_state(ctx, effect, STATE_ERR_INTERNAL); + return next_state(ctx, effect, STATE_CLOSED); fail_during_close: /* * We've broadcast close tx; if anything goes wrong, we just close * connection and wait. */ - set_effect(effect, stop_packets, true); + add_effect(&effect, stop_packets, true); /* Once close tx is deep enough, we consider it done. */ if (input_is(input, BITCOIN_CLOSE_DONE)) { - return STATE_CLOSED; + return next_state(ctx, effect, STATE_CLOSED); } else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) { /* A reorganization could make this happen. */ - set_effect(effect, broadcast, - bitcoin_spend_theirs(effect, sdata, idata->btc)); - set_effect(effect, watch, - bitcoin_watch(effect, effect->broadcast, + tx = bitcoin_spend_theirs(ctx, sdata, idata->btc); + add_effect(&effect, broadcast_tx, tx); + add_effect(&effect, watch, + bitcoin_watch(ctx, tx, BITCOIN_SPEND_THEIRS_DONE)); - htlcs = htlc_outputs_their_commit(effect, sdata, idata->btc, + htlcs = htlc_outputs_their_commit(ctx, sdata, idata->btc, BITCOIN_HTLC_TOUS_TIMEOUT, BITCOIN_HTLC_TOTHEM_SPENT, BITCOIN_HTLC_TOTHEM_TIMEOUT); @@ -1106,41 +1076,41 @@ fail_during_close: if (htlcs) { /* FIXME: Make sure caller uses INPUT_RVAL * if they were in the middle of FULFILL! */ - set_effect(effect, watch_htlcs, htlcs); - return STATE_CLOSE_WAIT_SPENDTHEM_CLOSE_WITH_HTLCS; + add_effect(&effect, watch_htlcs, htlcs); + return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM_CLOSE_WITH_HTLCS); } - return STATE_CLOSE_WAIT_SPENDTHEM_CLOSE; + return next_state(ctx, effect, STATE_CLOSE_WAIT_SPENDTHEM_CLOSE); } else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) { - steal = bitcoin_steal(effect, sdata, idata->btc); - if (!steal) - return STATE_ERR_INFORMATION_LEAK; - set_effect(effect, broadcast, steal); - set_effect(effect, watch, - bitcoin_watch(effect, effect->broadcast, - BITCOIN_STEAL_DONE)); + tx = bitcoin_steal(ctx, sdata, idata->btc); + if (!tx) + return next_state(ctx, effect, + STATE_ERR_INFORMATION_LEAK); + 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 STATE_CLOSE_WAIT_STEAL_CLOSE; + return next_state(ctx, effect, STATE_CLOSE_WAIT_STEAL_CLOSE); } else if (input_is(input, BITCOIN_ANCHOR_UNSPENT)) { - return STATE_ERR_ANCHOR_LOST; + return next_state(ctx, effect, STATE_ERR_ANCHOR_LOST); } - return STATE_ERR_INTERNAL; + return next_state(ctx, effect, STATE_ERR_INTERNAL); old_commit_spotted: /* * bitcoind reported a broadcast of the not-latest commit tx. */ - set_effect(effect, send, pkt_err(effect, "Otherspend noticed")); + add_effect(&effect, send_pkt, pkt_err(ctx, "Otherspend noticed")); /* No more packets, no more commands. */ - set_effect(effect, stop_packets, true); - set_effect(effect, stop_commands, true); + add_effect(&effect, stop_packets, true); + add_effect(&effect, stop_commands, true); /* If we can't find it, we're lost. */ - steal = bitcoin_steal(effect, sdata, idata->btc); - if (!steal) - return STATE_ERR_INFORMATION_LEAK; - set_effect(effect, broadcast, steal); - set_effect(effect, watch, - bitcoin_watch(effect, effect->broadcast, BITCOIN_STEAL_DONE)); - return STATE_CLOSE_WAIT_STEAL; + tx = bitcoin_steal(ctx, sdata, idata->btc); + if (!tx) + return next_state(ctx, 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); } diff --git a/state.h b/state.h index 822fa7681..7b5c2da0c 100644 --- a/state.h +++ b/state.h @@ -6,12 +6,34 @@ #include #include -enum cmd_complete_status { - CMD_STATUS_ONGOING, - CMD_STATUS_FAILED, - CMD_STATUS_SUCCESS, - CMD_STATUS_REQUEUE -}; +enum state_effect_type { + STATE_EFFECT_new_state, + STATE_EFFECT_in_error, + STATE_EFFECT_broadcast_tx, + 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, + STATE_EFFECT_stop_packets, + STATE_EFFECT_stop_commands, + /* FIXME: Use a watch for this?. */ + STATE_EFFECT_close_timeout, + STATE_EFFECT_htlc_in_progress, + STATE_EFFECT_update_theirsig, + STATE_EFFECT_htlc_abandon, + STATE_EFFECT_htlc_fulfill, + STATE_EFFECT_r_value, + /* FIXME: Combine into watches? */ + STATE_EFFECT_watch_htlcs, + STATE_EFFECT_unwatch_htlc, + STATE_EFFECT_watch_htlc_spend, + STATE_EFFECT_unwatch_htlc_spend +}; /* * This is the core state machine. @@ -20,69 +42,74 @@ enum cmd_complete_status { * and populates the "effect" struct with what it wants done. */ struct state_effect { - /* Transaction to broadcast. */ - struct bitcoin_tx *broadcast; + struct state_effect *next; - /* Packet to send. */ - Pkt *send; + enum state_effect_type etype; + union { + /* New state to enter. */ + enum state new_state; - /* Event to watch for. */ - struct watch *watch; + /* Transaction to broadcast. */ + struct bitcoin_tx *broadcast_tx; - /* Events to no longer watch for. */ - struct watch *unwatch; + /* Packet to send. */ + Pkt *send_pkt; - /* Defer an input. */ - enum state_input defer; + /* Event to watch for. */ + struct watch *watch; - /* Complete a command. */ - enum state_input complete; - enum cmd_complete_status status; - void *faildata; + /* Events to no longer watch for. */ + struct watch *unwatch; - /* Completing a CMD_CLOSE */ - enum cmd_complete_status close_status; + /* Defer an input. */ + enum state_input cmd_defer; - /* Stop taking packets? commands? */ - bool stop_packets, stop_commands; + /* Requeue/succeed/fail command. */ + enum state_input cmd_requeue; + enum state_input cmd_success; + void *cmd_fail; - /* Set a timeout for close tx. */ - enum state_input close_timeout; + /* CMD_CLOSE is complete (true if successful mutual close). */ + bool cmd_close_done; - /* Error received from other side. */ - Pkt *in_error; + /* Stop taking packets? commands? */ + bool stop_packets, stop_commands; - /* HTLC we're working on. */ - struct htlc_progress *htlc_in_progress; + /* Set a timeout for close tx. */ + enum state_input close_timeout; - /* Their signature for the new commit tx. */ - struct signature *update_theirsig; + /* Error received from other side. */ + Pkt *in_error; + + /* HTLC we're working on. */ + struct htlc_progress *htlc_in_progress; + + /* Their signature for the new commit tx. */ + struct signature *update_theirsig; - /* Stop working on HTLC. */ - bool htlc_abandon; + /* Stop working on HTLC. */ + bool htlc_abandon; - /* Finished working on HTLC. */ - bool htlc_fulfill; + /* Finished working on HTLC. */ + bool htlc_fulfill; - /* R value. */ - const struct htlc_rval *r_value; + /* R value. */ + const struct htlc_rval *r_value; - /* HTLC outputs to watch. */ - const struct htlc_watch *watch_htlcs; + /* HTLC outputs to watch. */ + const struct htlc_watch *watch_htlcs; - /* HTLC output to unwatch. */ - const struct htlc_unwatch *unwatch_htlc; + /* HTLC output to unwatch. */ + const struct htlc_unwatch *unwatch_htlc; - /* HTLC spends to watch/unwatch. */ - const struct htlc_spend_watch *watch_htlc_spend; - const struct htlc_spend_watch *unwatch_htlc_spend; + /* HTLC spends to watch/unwatch. */ + const struct htlc_spend_watch *watch_htlc_spend; + const struct htlc_spend_watch *unwatch_htlc_spend; - /* FIXME: More to come (for accept_*) */ + /* FIXME: More to come (for accept_*) */ + } u; }; -/* Initialize the above struct. */ -void state_effect_init(struct state_effect *effect); - static inline bool state_is_error(enum state s) { return s >= STATE_ERR_ANCHOR_TIMEOUT && s <= STATE_ERR_INTERNAL; @@ -103,9 +130,11 @@ union input { struct htlc_progress *htlc_prog; }; -enum state state(const enum state state, const struct state_data *sdata, - const enum state_input input, const union input *idata, - struct state_effect *effect); +struct state_effect *state(const tal_t *ctx, + const enum state state, + const struct state_data *sdata, + const enum state_input input, + const union input *idata); /* Any CMD_SEND_HTLC_* */ #define CMD_SEND_UPDATE_ANY INPUT_MAX @@ -154,58 +183,72 @@ Pkt *pkt_close_ack(const tal_t *ctx, const struct state_data *sdata); Pkt *unexpected_pkt(const tal_t *ctx, enum state_input input); /* Process various packets: return an error packet on failure. */ -Pkt *accept_pkt_open(struct state_effect *effect, +Pkt *accept_pkt_open(const tal_t *ctx, const struct state_data *sdata, - const Pkt *pkt); + const Pkt *pkt, + struct state_effect **effect); -Pkt *accept_pkt_anchor(struct state_effect *effect, +Pkt *accept_pkt_anchor(const tal_t *ctx, const struct state_data *sdata, - const Pkt *pkt); + const Pkt *pkt, + struct state_effect **effect); -Pkt *accept_pkt_open_commit_sig(struct state_effect *effect, - const struct state_data *sdata, const Pkt *pkt); +Pkt *accept_pkt_open_commit_sig(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect); -Pkt *accept_pkt_htlc_update(struct state_effect *effect, +Pkt *accept_pkt_htlc_update(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, Pkt **decline, - struct htlc_progress **htlcprog); + struct htlc_progress **htlcprog, + struct state_effect **effect); -Pkt *accept_pkt_htlc_routefail(struct state_effect *effect, +Pkt *accept_pkt_htlc_routefail(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, - struct htlc_progress **htlcprog); + struct htlc_progress **htlcprog, + struct state_effect **effect); -Pkt *accept_pkt_htlc_timedout(struct state_effect *effect, +Pkt *accept_pkt_htlc_timedout(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, - struct htlc_progress **htlcprog); + struct htlc_progress **htlcprog, + struct state_effect **effect); -Pkt *accept_pkt_htlc_fulfill(struct state_effect *effect, +Pkt *accept_pkt_htlc_fulfill(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct htlc_progress **htlcprog, + struct state_effect **effect); + +Pkt *accept_pkt_update_accept(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, - struct htlc_progress **htlcprog); + struct signature **sig, + struct state_effect **effect); -Pkt *accept_pkt_update_accept(struct state_effect *effect, - const struct state_data *sdata, const Pkt *pkt, - struct signature **sig); +Pkt *accept_pkt_update_complete(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect); -Pkt *accept_pkt_update_complete(struct state_effect *effect, - const struct state_data *sdata, const Pkt *pkt); - -Pkt *accept_pkt_update_signature(struct state_effect *effect, +Pkt *accept_pkt_update_signature(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, - struct signature **sig); + struct signature **sig, + struct state_effect **effect); -Pkt *accept_pkt_close(struct state_effect *effect, - const struct state_data *sdata, const Pkt *pkt); +Pkt *accept_pkt_close(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect); -Pkt *accept_pkt_close_complete(struct state_effect *effect, - const struct state_data *sdata, const Pkt *pkt); +Pkt *accept_pkt_close_complete(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect); -Pkt *accept_pkt_simultaneous_close(struct state_effect *effect, +Pkt *accept_pkt_simultaneous_close(const tal_t *ctx, const struct state_data *sdata, - const Pkt *pkt); + const Pkt *pkt, + struct state_effect **effect); -Pkt *accept_pkt_close_ack(struct state_effect *effect, - const struct state_data *sdata, const Pkt *pkt); +Pkt *accept_pkt_close_ack(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect); /** * committed_to_htlcs: do we have any locked-in HTLCs? @@ -252,26 +295,26 @@ struct watch *bitcoin_unwatch_anchor_depth(const tal_t *ctx, /** * bitcoin_watch_delayed: watch this (commit) tx, tell me when I can spend it - * @effect: the context to tal the watch off + * @ctx: the context to tal the watch off * @tx: the tx we're watching. * @canspend: the input to give when commit reaches spendable depth. * * Note that this tx may be malleated, as it's dual-signed. */ -struct watch *bitcoin_watch_delayed(const struct state_effect *effect, +struct watch *bitcoin_watch_delayed(const tal_t *ctx, const struct bitcoin_tx *tx, enum state_input canspend); /** * bitcoin_watch: watch this tx until it's "irreversible" - * @effect: the context to tal the watch off + * @ctx: the context to tal the watch off * @tx: the tx we're watching. * @done: the input to give when tx is completely buried. * * The tx should be immalleable by BIP62; once this fires we consider * the channel completely closed and stop watching (eg 100 txs down). */ -struct watch *bitcoin_watch(const struct state_effect *effect, +struct watch *bitcoin_watch(const tal_t *ctx, const struct bitcoin_tx *tx, enum state_input done); diff --git a/test/test_state_coverage.c b/test/test_state_coverage.c index 2e3410b76..9a65c5b8d 100644 --- a/test/test_state_coverage.c +++ b/test/test_state_coverage.c @@ -1,5 +1,6 @@ /* Test for state machine. */ #include +#include #include #include #include @@ -649,40 +650,47 @@ Pkt *unexpected_pkt(const tal_t *ctx, enum state_input input) return pkt_err(ctx, "Unexpected pkt"); } -Pkt *accept_pkt_open(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_open(const tal_t *ctx, + const struct state_data *sdata, + const Pkt *pkt, struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_OPEN)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } -Pkt *accept_pkt_anchor(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_anchor(const tal_t *ctx, + const struct state_data *sdata, + const Pkt *pkt, struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_ANCHOR)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } -Pkt *accept_pkt_open_commit_sig(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_open_commit_sig(const tal_t *ctx, + const struct state_data *sdata, + const Pkt *pkt, struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_OPEN_COMMIT_SIG)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } -Pkt *accept_pkt_htlc_update(struct state_effect *effect, +Pkt *accept_pkt_htlc_update(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, Pkt **decline, - struct htlc_progress **htlcprog) + struct htlc_progress **htlcprog, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_HTLC_UPDATE)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); if (fail(sdata, FAIL_DECLINE_HTLC)) - *decline = new_pkt(effect, PKT_UPDATE_DECLINE_HTLC); + *decline = new_pkt(ctx, PKT_UPDATE_DECLINE_HTLC); else { *decline = NULL; - *htlcprog = tal(effect, struct htlc_progress); + *htlcprog = tal(ctx, struct htlc_progress); /* If they propose it, it's to us. */ (*htlcprog)->htlc.to_them = false; (*htlcprog)->htlc.id = htlc_id_from_pkt(pkt); @@ -691,114 +699,132 @@ Pkt *accept_pkt_htlc_update(struct state_effect *effect, return NULL; } -Pkt *accept_pkt_htlc_routefail(struct state_effect *effect, +Pkt *accept_pkt_htlc_routefail(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, - struct htlc_progress **htlcprog) + struct htlc_progress **htlcprog, + struct state_effect **effect) { unsigned int id = htlc_id_from_pkt(pkt); const struct htlc *h = find_htlc(sdata, id); if (fail(sdata, FAIL_ACCEPT_HTLC_ROUTEFAIL)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); /* The shouldn't fail unless it's to them */ assert(h->to_them); - *htlcprog = tal(effect, struct htlc_progress); + *htlcprog = tal(ctx, struct htlc_progress); (*htlcprog)->htlc = *h; (*htlcprog)->adding = false; return NULL; } -Pkt *accept_pkt_htlc_timedout(struct state_effect *effect, +Pkt *accept_pkt_htlc_timedout(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, - struct htlc_progress **htlcprog) + struct htlc_progress **htlcprog, + struct state_effect **effect) { unsigned int id = htlc_id_from_pkt(pkt); const struct htlc *h = find_htlc(sdata, id); if (fail(sdata, FAIL_ACCEPT_HTLC_TIMEDOUT)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); /* The shouldn't timeout unless it's to us */ assert(!h->to_them); - *htlcprog = tal(effect, struct htlc_progress); + *htlcprog = tal(ctx, struct htlc_progress); (*htlcprog)->htlc = *h; (*htlcprog)->adding = false; return NULL; } -Pkt *accept_pkt_htlc_fulfill(struct state_effect *effect, - const struct state_data *sdata, const Pkt *pkt, - struct htlc_progress **htlcprog) +Pkt *accept_pkt_htlc_fulfill(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct htlc_progress **htlcprog, + struct state_effect **effect) { unsigned int id = htlc_id_from_pkt(pkt); const struct htlc *h = find_htlc(sdata, id); if (fail(sdata, FAIL_ACCEPT_HTLC_FULFILL)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); /* The shouldn't complete unless it's to them */ assert(h->to_them); - *htlcprog = tal(effect, struct htlc_progress); + *htlcprog = tal(ctx, struct htlc_progress); (*htlcprog)->htlc = *h; (*htlcprog)->adding = false; return NULL; } -Pkt *accept_pkt_update_accept(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt, struct signature **sig) +Pkt *accept_pkt_update_accept(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct signature **sig, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_UPDATE_ACCEPT)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); - *sig = (struct signature *)tal_strdup(effect, "from PKT_UPDATE_ACCEPT"); + *sig = (struct signature *)tal_strdup(ctx, "from PKT_UPDATE_ACCEPT"); return NULL; } -Pkt *accept_pkt_update_complete(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_update_complete(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_UPDATE_COMPLETE)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } -Pkt *accept_pkt_update_signature(struct state_effect *effect, +Pkt *accept_pkt_update_signature(const tal_t *ctx, const struct state_data *sdata, const Pkt *pkt, - struct signature **sig) + struct signature **sig, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_UPDATE_SIGNATURE)) - return pkt_err(effect, "Error inject"); - *sig = (struct signature *)tal_strdup(effect, "from PKT_UPDATE_SIGNATURE"); + return pkt_err(ctx, "Error inject"); + *sig = (struct signature *)tal_strdup(ctx, "from PKT_UPDATE_SIGNATURE"); return NULL; } -Pkt *accept_pkt_close(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_close(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_CLOSE)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } -Pkt *accept_pkt_close_complete(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_close_complete(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_CLOSE_COMPLETE)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } -Pkt *accept_pkt_simultaneous_close(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_simultaneous_close(const tal_t *ctx, + const struct state_data *sdata, + const Pkt *pkt, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_SIMULTANEOUS_CLOSE)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } -Pkt *accept_pkt_close_ack(struct state_effect *effect, const struct state_data *sdata, const Pkt *pkt) +Pkt *accept_pkt_close_ack(const tal_t *ctx, + const struct state_data *sdata, const Pkt *pkt, + struct state_effect **effect) { if (fail(sdata, FAIL_ACCEPT_CLOSE_ACK)) - return pkt_err(effect, "Error inject"); + return pkt_err(ctx, "Error inject"); return NULL; } @@ -905,11 +931,11 @@ struct watch *bitcoin_unwatch_anchor_depth(const tal_t *ctx, } /* Wait for our commit to be spendable. */ -struct watch *bitcoin_watch_delayed(const struct state_effect *effect, +struct watch *bitcoin_watch_delayed(const tal_t *ctx, const struct bitcoin_tx *tx, enum state_input canspend) { - struct watch *watch = talz(effect, struct watch); + struct watch *watch = talz(ctx, struct watch); assert(bitcoin_tx_is(tx, "our commit")); add_event(&watch->events, canspend); @@ -918,11 +944,11 @@ struct watch *bitcoin_watch_delayed(const struct state_effect *effect, /* Wait for commit to be very deeply buried (so we no longer need to * even watch) */ -struct watch *bitcoin_watch(const struct state_effect *effect, +struct watch *bitcoin_watch(const tal_t *ctx, const struct bitcoin_tx *tx, enum state_input done) { - struct watch *watch = talz(effect, struct watch); + struct watch *watch = talz(ctx, struct watch); if (done == BITCOIN_STEAL_DONE) assert(bitcoin_tx_is(tx, "steal")); @@ -1232,11 +1258,11 @@ static void init_trail(struct trail *t, static void update_trail(struct trail *t, const struct state_data *after, - const struct state_effect *effects) + const Pkt *output) { t->after = after; t->num_peer_outputs = after->peer->core.num_outputs; - t->pkt_sent = (const char *)effects->send; + t->pkt_sent = (const char *)output; } static void report_trail_rev(const struct trail *t) @@ -1382,11 +1408,62 @@ static bool rval_known(const struct state_data *sdata, unsigned int id) return false; } -static const char *apply_effects(struct state_data *sdata, - const struct state_effect *effect) +/* Some assertions once they've already been applied. */ +static char *check_effects(struct state_data *sdata, + const struct state_effect *effect) { - if (effect->send) { - const char *pkt = (const char *)effect->send; + while (effect) { + if (effect->etype == STATE_EFFECT_in_error) { + /* We should stop talking to them after error recvd. */ + if (sdata->core.pkt_inputs) + return "packets still open after error pkt"; + } else if (effect->etype == STATE_EFFECT_stop_commands) { + if (sdata->core.current_command != INPUT_NONE) + return tal_fmt(NULL, + "stop_commands with pending command %s", + input_name(sdata->core.current_command)); + if (sdata->core.closing_cmd) + return "stop_commands with pending CMD_CLOSE"; + } + effect = effect->next; + } + return NULL; +} + +/* We apply them backwards, which helps our assertions. It's not actually + * required. */ +static const char *apply_effects(struct state_data *sdata, + const struct state_effect *effect, + uint64_t *effects, + Pkt **output) +{ + const struct htlc *h; + + if (!effect) + return NULL; + + if (effect->next) { + const char *problem = apply_effects(sdata, effect->next, + effects, output); + if (problem) + return problem; + } + + if (*effects & (1ULL << effect->etype)) + return tal_fmt(NULL, "Effect %u twice", effect->etype); + *effects |= (1ULL << effect->etype); + + switch (effect->etype) { + case STATE_EFFECT_new_state: + sdata->core.state = effect->u.new_state; + break; + case STATE_EFFECT_in_error: + break; + case STATE_EFFECT_broadcast_tx: + break; + case STATE_EFFECT_send_pkt: { + const char *pkt = (const char *)effect->u.send_pkt; + *output = effect->u.send_pkt; /* Check for errors. */ if (strstarts(pkt, "ERROR_PKT:")) { @@ -1404,61 +1481,68 @@ static const char *apply_effects(struct state_data *sdata, sdata->core.outputs[sdata->core.num_outputs] = input_by_name(pkt); sdata->pkt_data[sdata->core.num_outputs++] - = htlc_id_from_pkt(effect->send); + = htlc_id_from_pkt(effect->u.send_pkt); + break; } - if (effect->watch) { - /* We can have multiple steals or spendtheirs in flight, - so make exceptions for BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */ + case STATE_EFFECT_watch: + /* We can have multiple steals or spendtheirs + in flight, so make exceptions for + BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */ if (sdata->core.event_notifies & (1ULL << BITCOIN_STEAL_DONE) - & effect->watch->events) - remove_event(&effect->watch->events, BITCOIN_STEAL_DONE); + & effect->u.watch->events) + remove_event(&effect->u.watch->events, BITCOIN_STEAL_DONE); if (sdata->core.event_notifies & (1ULL << BITCOIN_SPEND_THEIRS_DONE) - & effect->watch->events) - remove_event(&effect->watch->events, + & effect->u.watch->events) + remove_event(&effect->u.watch->events, BITCOIN_SPEND_THEIRS_DONE); - if (sdata->core.event_notifies & effect->watch->events) + if (sdata->core.event_notifies & effect->u.watch->events) return "event set twice"; - sdata->core.event_notifies |= effect->watch->events; - } - if (effect->unwatch) { - if ((sdata->core.event_notifies & effect->unwatch->events) - != effect->unwatch->events) + sdata->core.event_notifies |= effect->u.watch->events; + break; + case STATE_EFFECT_unwatch: + if ((sdata->core.event_notifies & effect->u.unwatch->events) + != effect->u.unwatch->events) return "unset event unwatched"; - sdata->core.event_notifies &= ~effect->unwatch->events; - } - if (effect->defer != INPUT_NONE) { + sdata->core.event_notifies &= ~effect->u.unwatch->events; + break; + case STATE_EFFECT_cmd_defer: /* If it was current command, it is no longer. */ - if (is_current_command(sdata, effect->defer)) + if (is_current_command(sdata, effect->u.cmd_defer)) sdata->core.current_command = INPUT_NONE; - else if (input_is_pkt(effect->defer)) { + else if (input_is_pkt(effect->u.cmd_defer)) { /* Unlike commands, which we always resubmit, * we have to remember deferred packets. */ /* We assume only one deferrment! */ assert(sdata->core.deferred_pkt == INPUT_NONE - || sdata->core.deferred_pkt == effect->defer); - sdata->core.deferred_pkt = effect->defer; + || sdata->core.deferred_pkt == effect->u.cmd_defer); + sdata->core.deferred_pkt = effect->u.cmd_defer; sdata->core.deferred_state = sdata->core.state; } - } - if (effect->complete != INPUT_NONE) { - if (!is_current_command(sdata, effect->complete)) { - return tal_fmt(NULL, "Completed %s not %s", - input_name(effect->complete), - input_name(sdata->core.current_command)); - } + break; + case STATE_EFFECT_cmd_requeue: + assert(is_current_command(sdata, effect->u.cmd_requeue)); sdata->core.current_command = INPUT_NONE; - } - if (effect->close_status != CMD_STATUS_ONGOING) { + break; + case STATE_EFFECT_cmd_success: + assert(is_current_command(sdata, effect->u.cmd_success)); + sdata->core.current_command = INPUT_NONE; + break; + case STATE_EFFECT_cmd_fail: + if (sdata->core.current_command == INPUT_NONE) + return "Failed command with none current"; + sdata->core.current_command = INPUT_NONE; + break; + case STATE_EFFECT_cmd_close_done: if (!sdata->core.closing_cmd) return tal_fmt(NULL, "%s but not closing", - effect->close_status == CMD_STATUS_SUCCESS ? "Success" - : "Failure"); + effect->u.cmd_close_done + ? "Success" : "Failure"); sdata->core.closing_cmd = false; - } - if (effect->stop_packets) { + break; + case STATE_EFFECT_stop_packets: if (!sdata->core.pkt_inputs) return "stop_packets twice"; sdata->core.pkt_inputs = false; @@ -1466,58 +1550,50 @@ static const char *apply_effects(struct state_data *sdata, /* Can no longer receive packet timeouts, either. */ remove_event_(&sdata->core.event_notifies, INPUT_CLOSE_COMPLETE_TIMEOUT); - } - if (effect->stop_commands) { + break; + case STATE_EFFECT_stop_commands: if (!sdata->core.cmd_inputs) return "stop_commands twice"; - if (sdata->core.current_command != INPUT_NONE) - return tal_fmt(NULL, "stop_commands with pending command %s", - input_name(sdata->core.current_command)); - if (sdata->core.closing_cmd) - return "stop_commands with pending CMD_CLOSE"; sdata->core.cmd_inputs = false; - } - if (effect->close_timeout != INPUT_NONE) { - add_event(&sdata->core.event_notifies, effect->close_timeout); + break; + case STATE_EFFECT_close_timeout: + add_event(&sdata->core.event_notifies, + effect->u.close_timeout); /* We assume this. */ - assert(effect->close_timeout == INPUT_CLOSE_COMPLETE_TIMEOUT); - } - if (effect->in_error) { - /* We should stop talking to them after error received. */ - if (sdata->core.pkt_inputs) - return "packets still open after error pkt"; - } - /* We can abandon and add a new one, if we're LOWPRIO */ - if (effect->htlc_abandon) { - if (sdata->current_htlc.htlc.id == -1) - return "HTLC not in progress, can't abandon"; - if (effect->htlc_fulfill) - return "Complete and abandon?"; - sdata->current_htlc.htlc.id = -1; - } - if (effect->htlc_in_progress) { + assert(effect->u.close_timeout + == INPUT_CLOSE_COMPLETE_TIMEOUT); + break; + case STATE_EFFECT_htlc_in_progress: if (sdata->current_htlc.htlc.id != -1) return "HTLC already in progress"; - if (effect->htlc_fulfill) - return "Complete in one step?"; - sdata->current_htlc = *effect->htlc_in_progress; - } - if (effect->htlc_fulfill) { + sdata->current_htlc = *effect->u.htlc_in_progress; + break; + case STATE_EFFECT_update_theirsig: + break; + case STATE_EFFECT_htlc_abandon: + if (sdata->current_htlc.htlc.id == -1) + return "HTLC not in progress, can't abandon"; + sdata->current_htlc.htlc.id = -1; + break; + case STATE_EFFECT_htlc_fulfill: if (sdata->current_htlc.htlc.id == -1) return "HTLC not in progress, can't complete"; if (sdata->current_htlc.adding) { - add_htlc(sdata->htlcs_to_us, &sdata->num_htlcs_to_us, + add_htlc(sdata->htlcs_to_us, + &sdata->num_htlcs_to_us, sdata->htlcs_to_them, &sdata->num_htlcs_to_them, ARRAY_SIZE(sdata->htlcs_to_us), &sdata->current_htlc.htlc); } else { const struct htlc *h; - h = find_htlc(sdata, sdata->current_htlc.htlc.id); + h = find_htlc(sdata, + sdata->current_htlc.htlc.id); if (!h) return "Removing nonexistent HTLC?"; - if (h->to_them != sdata->current_htlc.htlc.to_them) + if (h->to_them != + sdata->current_htlc.htlc.to_them) return "Removing disagreed about to_them"; remove_htlc(sdata->htlcs_to_us, &sdata->num_htlcs_to_us, sdata->htlcs_to_them, @@ -1526,81 +1602,46 @@ static const char *apply_effects(struct state_data *sdata, h); } sdata->current_htlc.htlc.id = -1; - } - - if (effect->r_value) { - /* We set r_value when they spend an HTLC, so we can set this - * multiple times (multiple commit txs) */ - if (!rval_known(sdata, effect->r_value->id)) { + break; + case STATE_EFFECT_r_value: + /* We set r_value when they spend an HTLC, so + * we can set this multiple times (multiple commit + * txs) */ + if (!rval_known(sdata, effect->u.r_value->id)) { if (sdata->num_rvals_known == ARRAY_SIZE(sdata->rvals_known)) return "Too many rvals"; sdata->rvals_known[sdata->num_rvals_known++] - = effect->r_value->id; + = effect->u.r_value->id; } - } - - if (effect->watch_htlcs) { + break; + + case STATE_EFFECT_watch_htlcs: assert(sdata->num_live_htlcs_to_us - + effect->watch_htlcs->num_htlcs_to_us + + effect->u.watch_htlcs->num_htlcs_to_us <= ARRAY_SIZE(sdata->live_htlcs_to_us)); assert(sdata->num_live_htlcs_to_them - + effect->watch_htlcs->num_htlcs_to_them + + effect->u.watch_htlcs->num_htlcs_to_them <= ARRAY_SIZE(sdata->live_htlcs_to_them)); memcpy(sdata->live_htlcs_to_us + sdata->num_live_htlcs_to_us, - effect->watch_htlcs->htlcs_to_us, - effect->watch_htlcs->num_htlcs_to_us - * sizeof(effect->watch_htlcs->htlcs_to_us[0])); + effect->u.watch_htlcs->htlcs_to_us, + effect->u.watch_htlcs->num_htlcs_to_us + * sizeof(effect->u.watch_htlcs->htlcs_to_us[0])); memcpy(sdata->live_htlcs_to_them + sdata->num_live_htlcs_to_them, - effect->watch_htlcs->htlcs_to_them, - effect->watch_htlcs->num_htlcs_to_them - * sizeof(effect->watch_htlcs->htlcs_to_them[0])); + effect->u.watch_htlcs->htlcs_to_them, + effect->u.watch_htlcs->num_htlcs_to_them + * sizeof(effect->u.watch_htlcs->htlcs_to_them[0])); sdata->num_live_htlcs_to_us - += effect->watch_htlcs->num_htlcs_to_us; + += effect->u.watch_htlcs->num_htlcs_to_us; sdata->num_live_htlcs_to_them - += effect->watch_htlcs->num_htlcs_to_them; + += effect->u.watch_htlcs->num_htlcs_to_them; /* Can happen if we were finished, then new commit tx */ remove_event_(&sdata->core.event_notifies, INPUT_NO_MORE_HTLCS); - } - if (effect->watch_htlc_spend) { - const struct htlc *h; - - h = find_live_htlc(sdata, effect->watch_htlc_spend->id); - add_htlc(sdata->htlc_spends_to_us, &sdata->num_htlc_spends_to_us, - sdata->htlc_spends_to_them, - &sdata->num_htlc_spends_to_them, - ARRAY_SIZE(sdata->htlc_spends_to_us), - h); - - /* We assume this */ - if (h->to_them) - assert(effect->watch_htlc_spend->done - == BITCOIN_HTLC_RETURN_SPEND_DONE); - else - assert(effect->watch_htlc_spend->done - == BITCOIN_HTLC_FULFILL_SPEND_DONE); - } - if (effect->unwatch_htlc_spend) { - const struct htlc *h; - - h = find_htlc_spend(sdata, effect->unwatch_htlc_spend->id); - remove_htlc(sdata->htlc_spends_to_us, - &sdata->num_htlc_spends_to_us, - sdata->htlc_spends_to_them, - &sdata->num_htlc_spends_to_them, - ARRAY_SIZE(sdata->htlc_spends_to_us), - h); - if (!outstanding_htlc_watches(sdata)) { - assert(effect->unwatch_htlc_spend->done - == INPUT_NO_MORE_HTLCS); - add_event(&sdata->core.event_notifies, - effect->unwatch_htlc_spend->done); - } - } - if (effect->unwatch_htlc) { + break; + case STATE_EFFECT_unwatch_htlc: /* Unwatch all? */ - if (effect->unwatch_htlc->id == -1) { + if (effect->u.unwatch_htlc->id == -1) { /* This can happen if we get in front of * INPUT_NO_MORE_HTLCS */ if (!outstanding_htlc_watches(sdata) @@ -1614,7 +1655,7 @@ static const char *apply_effects(struct state_data *sdata, } else { const struct htlc *h; - h = find_live_htlc(sdata, effect->unwatch_htlc->id); + h = find_live_htlc(sdata, effect->u.unwatch_htlc->id); /* That can fail, when we see them spend (and * thus stop watching) after we've timed out, @@ -1629,17 +1670,65 @@ static const char *apply_effects(struct state_data *sdata, /* If that was last, fire INPUT_NO_MORE_HTLCS */ if (!outstanding_htlc_watches(sdata)) { - assert(effect->unwatch_htlc->all_done + assert(effect->u.unwatch_htlc->all_done == INPUT_NO_MORE_HTLCS); add_event(&sdata->core.event_notifies, - effect->unwatch_htlc->all_done); + effect->u.unwatch_htlc->all_done); } } } + break; + case STATE_EFFECT_watch_htlc_spend: + h = find_live_htlc(sdata, effect->u.watch_htlc_spend->id); + add_htlc(sdata->htlc_spends_to_us, &sdata->num_htlc_spends_to_us, + sdata->htlc_spends_to_them, + &sdata->num_htlc_spends_to_them, + ARRAY_SIZE(sdata->htlc_spends_to_us), + h); + + /* We assume this */ + if (h->to_them) + assert(effect->u.watch_htlc_spend->done + == BITCOIN_HTLC_RETURN_SPEND_DONE); + else + assert(effect->u.watch_htlc_spend->done + == BITCOIN_HTLC_FULFILL_SPEND_DONE); + break; + case STATE_EFFECT_unwatch_htlc_spend: + h = find_htlc_spend(sdata, effect->u.unwatch_htlc_spend->id); + remove_htlc(sdata->htlc_spends_to_us, + &sdata->num_htlc_spends_to_us, + sdata->htlc_spends_to_them, + &sdata->num_htlc_spends_to_them, + ARRAY_SIZE(sdata->htlc_spends_to_us), + h); + if (!outstanding_htlc_watches(sdata)) { + assert(effect->u.unwatch_htlc_spend->done + == INPUT_NO_MORE_HTLCS); + add_event(&sdata->core.event_notifies, + effect->u.unwatch_htlc_spend->done); + } + break; + default: + return tal_fmt(NULL, "Unknown effect %u", effect->etype); } + return NULL; } +static const char *apply_all_effects(struct state_data *sdata, + const struct state_effect *effect, + Pkt **output) +{ + const char *problem; + uint64_t effects = 0; + *output = NULL; + problem = apply_effects(sdata, effect, &effects, output); + if (!problem) + problem = check_effects(sdata, effect); + return problem; +} + static void eliminate_input(enum state_input **inputs, enum state_input in) { size_t i, n = tal_count(*inputs); @@ -1820,6 +1909,34 @@ static bool has_packets(const struct state_data *sdata) || sdata->core.num_outputs != 0; } +static struct state_effect *get_effect(const struct state_effect *effect, + enum state_effect_type type) +{ + while (effect) { + if (effect->etype == type) + break; + effect = effect->next; + } + return cast_const(struct state_effect *, effect); +} + +static enum state get_state_effect(const struct state_effect *effect, + enum state current) +{ + effect = get_effect(effect, STATE_EFFECT_new_state); + if (effect) + return effect->u.new_state; + return current; +} + +static Pkt *get_send_pkt(const struct state_effect *effect) +{ + effect = get_effect(effect, STATE_EFFECT_send_pkt); + if (effect) + return effect->u.send_pkt; + return NULL; +} + static void try_input(const struct state_data *sdata, enum state_input i, const union input *idata, @@ -1829,11 +1946,12 @@ static void try_input(const struct state_data *sdata, { struct state_data copy, peer; struct trail t; - struct state_effect *effect = tal(NULL, struct state_effect); enum state newstate; + struct state_effect *effect; const char *problem; + Pkt *output; + const tal_t *ctx = tal(NULL, char); - state_effect_init(effect); copy_peers(©, &peer, sdata); copy.current_input = i; @@ -1842,7 +1960,9 @@ static void try_input(const struct state_data *sdata, copy.trail = &t; eliminate_input(&hist->inputs_per_state[copy.core.state], i); - newstate = state(copy.core.state, ©, i, idata, effect); + effect = state(ctx, copy.core.state, ©, i, idata); + + newstate = get_state_effect(effect, sdata->core.state); normalpath &= normal_path(i, sdata->core.state, newstate); errorpath |= error_path(i, sdata->core.state, newstate); @@ -1862,13 +1982,12 @@ static void try_input(const struct state_data *sdata, newstr = state_name(newstate) + 6; } if (newstr != oldstr || include_nops) - add_dot(&hist->edges, oldstr, newstr, i, effect->send); + add_dot(&hist->edges, oldstr, newstr, i, + get_send_pkt(effect)); } - copy.core.state = newstate; - - problem = apply_effects(©, effect); - update_trail(&t, ©, effect); + problem = apply_all_effects(©, effect, &output); + update_trail(&t, ©, output); if (problem) report_trail(&t, problem); @@ -1879,14 +1998,14 @@ static void try_input(const struct state_data *sdata, /* Record any output. */ - if (effect->send) { + if (output) { record_output(&hist->outputs, - input_by_name((const char *)effect->send)); + input_by_name((const char *)output)); } if (hist->state_dump) { record_state(&hist->state_dump[sdata->core.state], i, newstate, - (const char *)effect->send); + (const char *)output); } /* Have we been in this overall situation before? */ @@ -1899,13 +2018,14 @@ static void try_input(const struct state_data *sdata, * * And if we're being quick, always stop. */ - if (effect->defer != INPUT_NONE + if (quick + || get_effect(effect, STATE_EFFECT_cmd_defer) || newstate == STATE_NORMAL_LOWPRIO || newstate == STATE_NORMAL_HIGHPRIO || i == BITCOIN_ANCHOR_OTHERSPEND || i == BITCOIN_ANCHOR_THEIRSPEND || quick) { - tal_free(effect); + tal_free(ctx); return; } if (t.depth > STATE_MAX * 10) @@ -1914,7 +2034,7 @@ static void try_input(const struct state_data *sdata, /* Don't continue if we reached a different error state. */ if (state_is_error(newstate)) { - tal_free(effect); + tal_free(ctx); return; } @@ -1942,7 +2062,7 @@ static void try_input(const struct state_data *sdata, if (outstanding_htlc_watches(©)) report_trail(&t, "CLOSED but watching HTLCs?"); - tal_free(effect); + tal_free(ctx); return; } @@ -1952,7 +2072,7 @@ static void try_input(const struct state_data *sdata, /* Don't bother running other peer we can't communicate. */ if (copy.core.pkt_inputs || peer.core.pkt_inputs) run_peer(&peer, normalpath, errorpath, &t, hist); - tal_free(effect); + tal_free(ctx); } static void sanity_check(const struct state_data *sdata) @@ -2230,22 +2350,21 @@ static enum state_input **map_inputs(void) { enum state_input **inps = tal_arr(NULL, enum state_input *, STATE_MAX); unsigned int i; - struct state_effect *effect = tal(inps, struct state_effect); + const tal_t *ctx = tal(NULL, char); for (i = 0; i < STATE_MAX; i++) { /* This is a global */ mapping_inputs = tal_arr(inps, enum state_input, 0); - state_effect_init(effect); /* This adds to mapping_inputs every input_is() call */ if (!state_is_error(i)) - state(i, NULL, INPUT_NONE, NULL, effect); + state(ctx, i, NULL, INPUT_NONE, NULL); inps[i] = mapping_inputs; } /* Reset global */ mapping_inputs = NULL; - tal_free(effect); + tal_free(ctx); return inps; }