Browse Source

state: simplify effect.

Make it a linked list of effects, rather than one big union.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
85f4a7cf14
  1. 850
      state.c
  2. 213
      state.h
  3. 515
      test/test_state_coverage.c

850
state.c

File diff suppressed because it is too large

213
state.h

@ -6,12 +6,34 @@
#include <state_types.h> #include <state_types.h>
#include <stdbool.h> #include <stdbool.h>
enum cmd_complete_status { enum state_effect_type {
CMD_STATUS_ONGOING, STATE_EFFECT_new_state,
CMD_STATUS_FAILED, STATE_EFFECT_in_error,
CMD_STATUS_SUCCESS, STATE_EFFECT_broadcast_tx,
CMD_STATUS_REQUEUE 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. * This is the core state machine.
@ -20,69 +42,74 @@ enum cmd_complete_status {
* and populates the "effect" struct with what it wants done. * and populates the "effect" struct with what it wants done.
*/ */
struct state_effect { struct state_effect {
/* Transaction to broadcast. */ struct state_effect *next;
struct bitcoin_tx *broadcast;
enum state_effect_type etype;
union {
/* New state to enter. */
enum state new_state;
/* Transaction to broadcast. */
struct bitcoin_tx *broadcast_tx;
/* Packet to send. */ /* Packet to send. */
Pkt *send; Pkt *send_pkt;
/* Event to watch for. */ /* Event to watch for. */
struct watch *watch; struct watch *watch;
/* Events to no longer watch for. */ /* Events to no longer watch for. */
struct watch *unwatch; struct watch *unwatch;
/* Defer an input. */ /* Defer an input. */
enum state_input defer; enum state_input cmd_defer;
/* Complete a command. */ /* Requeue/succeed/fail command. */
enum state_input complete; enum state_input cmd_requeue;
enum cmd_complete_status status; enum state_input cmd_success;
void *faildata; void *cmd_fail;
/* Completing a CMD_CLOSE */ /* CMD_CLOSE is complete (true if successful mutual close). */
enum cmd_complete_status close_status; bool cmd_close_done;
/* Stop taking packets? commands? */ /* Stop taking packets? commands? */
bool stop_packets, stop_commands; bool stop_packets, stop_commands;
/* Set a timeout for close tx. */ /* Set a timeout for close tx. */
enum state_input close_timeout; enum state_input close_timeout;
/* Error received from other side. */ /* Error received from other side. */
Pkt *in_error; Pkt *in_error;
/* HTLC we're working on. */ /* HTLC we're working on. */
struct htlc_progress *htlc_in_progress; struct htlc_progress *htlc_in_progress;
/* Their signature for the new commit tx. */ /* Their signature for the new commit tx. */
struct signature *update_theirsig; struct signature *update_theirsig;
/* Stop working on HTLC. */ /* Stop working on HTLC. */
bool htlc_abandon; bool htlc_abandon;
/* Finished working on HTLC. */ /* Finished working on HTLC. */
bool htlc_fulfill; bool htlc_fulfill;
/* R value. */ /* R value. */
const struct htlc_rval *r_value; const struct htlc_rval *r_value;
/* HTLC outputs to watch. */ /* HTLC outputs to watch. */
const struct htlc_watch *watch_htlcs; const struct htlc_watch *watch_htlcs;
/* HTLC output to unwatch. */ /* HTLC output to unwatch. */
const struct htlc_unwatch *unwatch_htlc; const struct htlc_unwatch *unwatch_htlc;
/* HTLC spends to watch/unwatch. */ /* HTLC spends to watch/unwatch. */
const struct htlc_spend_watch *watch_htlc_spend; const struct htlc_spend_watch *watch_htlc_spend;
const struct htlc_spend_watch *unwatch_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) static inline bool state_is_error(enum state s)
{ {
return s >= STATE_ERR_ANCHOR_TIMEOUT && s <= STATE_ERR_INTERNAL; return s >= STATE_ERR_ANCHOR_TIMEOUT && s <= STATE_ERR_INTERNAL;
@ -103,9 +130,11 @@ union input {
struct htlc_progress *htlc_prog; struct htlc_progress *htlc_prog;
}; };
enum state state(const enum state state, const struct state_data *sdata, struct state_effect *state(const tal_t *ctx,
const enum state_input input, const union input *idata, const enum state state,
struct state_effect *effect); const struct state_data *sdata,
const enum state_input input,
const union input *idata);
/* Any CMD_SEND_HTLC_* */ /* Any CMD_SEND_HTLC_* */
#define CMD_SEND_UPDATE_ANY INPUT_MAX #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); Pkt *unexpected_pkt(const tal_t *ctx, enum state_input input);
/* Process various packets: return an error packet on failure. */ /* 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 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 struct state_data *sdata,
const Pkt *pkt); const Pkt *pkt,
struct state_effect **effect);
Pkt *accept_pkt_open_commit_sig(struct state_effect *effect, Pkt *accept_pkt_open_commit_sig(const tal_t *ctx,
const struct state_data *sdata, const Pkt *pkt); 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, const struct state_data *sdata, const Pkt *pkt,
Pkt **decline, 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, 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, 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, const struct state_data *sdata, const Pkt *pkt,
struct htlc_progress **htlcprog); struct htlc_progress **htlcprog,
struct state_effect **effect);
Pkt *accept_pkt_update_accept(struct state_effect *effect, Pkt *accept_pkt_update_accept(const tal_t *ctx,
const struct state_data *sdata, const Pkt *pkt, const struct state_data *sdata, const Pkt *pkt,
struct signature **sig); struct signature **sig,
struct state_effect **effect);
Pkt *accept_pkt_update_complete(struct state_effect *effect, Pkt *accept_pkt_update_complete(const tal_t *ctx,
const struct state_data *sdata, const Pkt *pkt); const struct state_data *sdata, const Pkt *pkt,
struct state_effect **effect);
Pkt *accept_pkt_update_signature(struct state_effect *effect, Pkt *accept_pkt_update_signature(const tal_t *ctx,
const struct state_data *sdata, const struct state_data *sdata,
const Pkt *pkt, const Pkt *pkt,
struct signature **sig); struct signature **sig,
struct state_effect **effect);
Pkt *accept_pkt_close(struct state_effect *effect, Pkt *accept_pkt_close(const tal_t *ctx,
const struct state_data *sdata, const Pkt *pkt); const struct state_data *sdata, const Pkt *pkt,
struct state_effect **effect);
Pkt *accept_pkt_close_complete(struct state_effect *effect, Pkt *accept_pkt_close_complete(const tal_t *ctx,
const struct state_data *sdata, const Pkt *pkt); 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 struct state_data *sdata,
const Pkt *pkt); const Pkt *pkt,
struct state_effect **effect);
Pkt *accept_pkt_close_ack(struct state_effect *effect, Pkt *accept_pkt_close_ack(const tal_t *ctx,
const struct state_data *sdata, const Pkt *pkt); const struct state_data *sdata, const Pkt *pkt,
struct state_effect **effect);
/** /**
* committed_to_htlcs: do we have any locked-in HTLCs? * 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 * 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. * @tx: the tx we're watching.
* @canspend: the input to give when commit reaches spendable depth. * @canspend: the input to give when commit reaches spendable depth.
* *
* Note that this tx may be malleated, as it's dual-signed. * 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, const struct bitcoin_tx *tx,
enum state_input canspend); enum state_input canspend);
/** /**
* bitcoin_watch: watch this tx until it's "irreversible" * 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. * @tx: the tx we're watching.
* @done: the input to give when tx is completely buried. * @done: the input to give when tx is completely buried.
* *
* The tx should be immalleable by BIP62; once this fires we consider * The tx should be immalleable by BIP62; once this fires we consider
* the channel completely closed and stop watching (eg 100 txs down). * 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, const struct bitcoin_tx *tx,
enum state_input done); enum state_input done);

515
test/test_state_coverage.c

@ -1,5 +1,6 @@
/* Test for state machine. */ /* Test for state machine. */
#include <stdbool.h> #include <stdbool.h>
#include <ccan/cast/cast.h>
#include <ccan/array_size/array_size.h> #include <ccan/array_size/array_size.h>
#include <ccan/tal/tal.h> #include <ccan/tal/tal.h>
#include <ccan/tal/str/str.h> #include <ccan/tal/str/str.h>
@ -649,40 +650,47 @@ Pkt *unexpected_pkt(const tal_t *ctx, enum state_input input)
return pkt_err(ctx, "Unexpected pkt"); 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)) if (fail(sdata, FAIL_ACCEPT_OPEN))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; 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)) if (fail(sdata, FAIL_ACCEPT_ANCHOR))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; 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)) if (fail(sdata, FAIL_ACCEPT_OPEN_COMMIT_SIG))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; 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, const struct state_data *sdata, const Pkt *pkt,
Pkt **decline, Pkt **decline,
struct htlc_progress **htlcprog) struct htlc_progress **htlcprog,
struct state_effect **effect)
{ {
if (fail(sdata, FAIL_ACCEPT_HTLC_UPDATE)) 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)) if (fail(sdata, FAIL_DECLINE_HTLC))
*decline = new_pkt(effect, PKT_UPDATE_DECLINE_HTLC); *decline = new_pkt(ctx, PKT_UPDATE_DECLINE_HTLC);
else { else {
*decline = NULL; *decline = NULL;
*htlcprog = tal(effect, struct htlc_progress); *htlcprog = tal(ctx, struct htlc_progress);
/* If they propose it, it's to us. */ /* If they propose it, it's to us. */
(*htlcprog)->htlc.to_them = false; (*htlcprog)->htlc.to_them = false;
(*htlcprog)->htlc.id = htlc_id_from_pkt(pkt); (*htlcprog)->htlc.id = htlc_id_from_pkt(pkt);
@ -691,114 +699,132 @@ Pkt *accept_pkt_htlc_update(struct state_effect *effect,
return NULL; 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, 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); unsigned int id = htlc_id_from_pkt(pkt);
const struct htlc *h = find_htlc(sdata, id); const struct htlc *h = find_htlc(sdata, id);
if (fail(sdata, FAIL_ACCEPT_HTLC_ROUTEFAIL)) 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 */ /* The shouldn't fail unless it's to them */
assert(h->to_them); assert(h->to_them);
*htlcprog = tal(effect, struct htlc_progress); *htlcprog = tal(ctx, struct htlc_progress);
(*htlcprog)->htlc = *h; (*htlcprog)->htlc = *h;
(*htlcprog)->adding = false; (*htlcprog)->adding = false;
return NULL; 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, 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); unsigned int id = htlc_id_from_pkt(pkt);
const struct htlc *h = find_htlc(sdata, id); const struct htlc *h = find_htlc(sdata, id);
if (fail(sdata, FAIL_ACCEPT_HTLC_TIMEDOUT)) 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 */ /* The shouldn't timeout unless it's to us */
assert(!h->to_them); assert(!h->to_them);
*htlcprog = tal(effect, struct htlc_progress); *htlcprog = tal(ctx, struct htlc_progress);
(*htlcprog)->htlc = *h; (*htlcprog)->htlc = *h;
(*htlcprog)->adding = false; (*htlcprog)->adding = false;
return NULL; return NULL;
} }
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, 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); unsigned int id = htlc_id_from_pkt(pkt);
const struct htlc *h = find_htlc(sdata, id); const struct htlc *h = find_htlc(sdata, id);
if (fail(sdata, FAIL_ACCEPT_HTLC_FULFILL)) 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 */ /* The shouldn't complete unless it's to them */
assert(h->to_them); assert(h->to_them);
*htlcprog = tal(effect, struct htlc_progress); *htlcprog = tal(ctx, struct htlc_progress);
(*htlcprog)->htlc = *h; (*htlcprog)->htlc = *h;
(*htlcprog)->adding = false; (*htlcprog)->adding = false;
return NULL; 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)) 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; 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)) if (fail(sdata, FAIL_ACCEPT_UPDATE_COMPLETE))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; 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, 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)) if (fail(sdata, FAIL_ACCEPT_UPDATE_SIGNATURE))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
*sig = (struct signature *)tal_strdup(effect, "from PKT_UPDATE_SIGNATURE"); *sig = (struct signature *)tal_strdup(ctx, "from PKT_UPDATE_SIGNATURE");
return NULL; 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)) if (fail(sdata, FAIL_ACCEPT_CLOSE))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; 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)) if (fail(sdata, FAIL_ACCEPT_CLOSE_COMPLETE))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; 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)) if (fail(sdata, FAIL_ACCEPT_SIMULTANEOUS_CLOSE))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; 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)) if (fail(sdata, FAIL_ACCEPT_CLOSE_ACK))
return pkt_err(effect, "Error inject"); return pkt_err(ctx, "Error inject");
return NULL; return NULL;
} }
@ -905,11 +931,11 @@ struct watch *bitcoin_unwatch_anchor_depth(const tal_t *ctx,
} }
/* Wait for our commit to be spendable. */ /* 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, const struct bitcoin_tx *tx,
enum state_input canspend) 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")); assert(bitcoin_tx_is(tx, "our commit"));
add_event(&watch->events, canspend); 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 /* Wait for commit to be very deeply buried (so we no longer need to
* even watch) */ * even watch) */
struct watch *bitcoin_watch(const struct state_effect *effect, struct watch *bitcoin_watch(const tal_t *ctx,
const struct bitcoin_tx *tx, const struct bitcoin_tx *tx,
enum state_input done) enum state_input done)
{ {
struct watch *watch = talz(effect, struct watch); struct watch *watch = talz(ctx, struct watch);
if (done == BITCOIN_STEAL_DONE) if (done == BITCOIN_STEAL_DONE)
assert(bitcoin_tx_is(tx, "steal")); assert(bitcoin_tx_is(tx, "steal"));
@ -1232,11 +1258,11 @@ static void init_trail(struct trail *t,
static void update_trail(struct trail *t, static void update_trail(struct trail *t,
const struct state_data *after, const struct state_data *after,
const struct state_effect *effects) const Pkt *output)
{ {
t->after = after; t->after = after;
t->num_peer_outputs = after->peer->core.num_outputs; 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) 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; return false;
} }
/* Some assertions once they've already been applied. */
static char *check_effects(struct state_data *sdata,
const struct state_effect *effect)
{
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, static const char *apply_effects(struct state_data *sdata,
const struct state_effect *effect) const struct state_effect *effect,
uint64_t *effects,
Pkt **output)
{ {
if (effect->send) { const struct htlc *h;
const char *pkt = (const char *)effect->send;
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. */ /* Check for errors. */
if (strstarts(pkt, "ERROR_PKT:")) { 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] sdata->core.outputs[sdata->core.num_outputs]
= input_by_name(pkt); = input_by_name(pkt);
sdata->pkt_data[sdata->core.num_outputs++] 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) { case STATE_EFFECT_watch:
/* We can have multiple steals or spendtheirs in flight, /* We can have multiple steals or spendtheirs
so make exceptions for BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */ in flight, so make exceptions for
BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */
if (sdata->core.event_notifies & (1ULL << BITCOIN_STEAL_DONE) if (sdata->core.event_notifies & (1ULL << BITCOIN_STEAL_DONE)
& effect->watch->events) & effect->u.watch->events)
remove_event(&effect->watch->events, BITCOIN_STEAL_DONE); remove_event(&effect->u.watch->events, BITCOIN_STEAL_DONE);
if (sdata->core.event_notifies if (sdata->core.event_notifies
& (1ULL << BITCOIN_SPEND_THEIRS_DONE) & (1ULL << BITCOIN_SPEND_THEIRS_DONE)
& effect->watch->events) & effect->u.watch->events)
remove_event(&effect->watch->events, remove_event(&effect->u.watch->events,
BITCOIN_SPEND_THEIRS_DONE); 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"; return "event set twice";
sdata->core.event_notifies |= effect->watch->events; sdata->core.event_notifies |= effect->u.watch->events;
} break;
if (effect->unwatch) { case STATE_EFFECT_unwatch:
if ((sdata->core.event_notifies & effect->unwatch->events) if ((sdata->core.event_notifies & effect->u.unwatch->events)
!= effect->unwatch->events) != effect->u.unwatch->events)
return "unset event unwatched"; return "unset event unwatched";
sdata->core.event_notifies &= ~effect->unwatch->events; sdata->core.event_notifies &= ~effect->u.unwatch->events;
} break;
if (effect->defer != INPUT_NONE) { case STATE_EFFECT_cmd_defer:
/* If it was current command, it is no longer. */ /* 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; 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, /* Unlike commands, which we always resubmit,
* we have to remember deferred packets. */ * we have to remember deferred packets. */
/* We assume only one deferrment! */ /* We assume only one deferrment! */
assert(sdata->core.deferred_pkt == INPUT_NONE assert(sdata->core.deferred_pkt == INPUT_NONE
|| sdata->core.deferred_pkt == effect->defer); || sdata->core.deferred_pkt == effect->u.cmd_defer);
sdata->core.deferred_pkt = effect->defer; sdata->core.deferred_pkt = effect->u.cmd_defer;
sdata->core.deferred_state = sdata->core.state; sdata->core.deferred_state = sdata->core.state;
} }
} break;
if (effect->complete != INPUT_NONE) { case STATE_EFFECT_cmd_requeue:
if (!is_current_command(sdata, effect->complete)) { assert(is_current_command(sdata, effect->u.cmd_requeue));
return tal_fmt(NULL, "Completed %s not %s",
input_name(effect->complete),
input_name(sdata->core.current_command));
}
sdata->core.current_command = INPUT_NONE; sdata->core.current_command = INPUT_NONE;
} break;
if (effect->close_status != CMD_STATUS_ONGOING) { 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) if (!sdata->core.closing_cmd)
return tal_fmt(NULL, "%s but not closing", return tal_fmt(NULL, "%s but not closing",
effect->close_status == CMD_STATUS_SUCCESS ? "Success" effect->u.cmd_close_done
: "Failure"); ? "Success" : "Failure");
sdata->core.closing_cmd = false; sdata->core.closing_cmd = false;
} break;
if (effect->stop_packets) { case STATE_EFFECT_stop_packets:
if (!sdata->core.pkt_inputs) if (!sdata->core.pkt_inputs)
return "stop_packets twice"; return "stop_packets twice";
sdata->core.pkt_inputs = false; 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. */ /* Can no longer receive packet timeouts, either. */
remove_event_(&sdata->core.event_notifies, remove_event_(&sdata->core.event_notifies,
INPUT_CLOSE_COMPLETE_TIMEOUT); INPUT_CLOSE_COMPLETE_TIMEOUT);
} break;
if (effect->stop_commands) { case STATE_EFFECT_stop_commands:
if (!sdata->core.cmd_inputs) if (!sdata->core.cmd_inputs)
return "stop_commands twice"; 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; sdata->core.cmd_inputs = false;
} break;
if (effect->close_timeout != INPUT_NONE) { case STATE_EFFECT_close_timeout:
add_event(&sdata->core.event_notifies, effect->close_timeout); add_event(&sdata->core.event_notifies,
effect->u.close_timeout);
/* We assume this. */ /* We assume this. */
assert(effect->close_timeout == INPUT_CLOSE_COMPLETE_TIMEOUT); assert(effect->u.close_timeout
} == INPUT_CLOSE_COMPLETE_TIMEOUT);
if (effect->in_error) { break;
/* We should stop talking to them after error received. */ case STATE_EFFECT_htlc_in_progress:
if (sdata->core.pkt_inputs) if (sdata->current_htlc.htlc.id != -1)
return "packets still open after error pkt"; return "HTLC already in progress";
} sdata->current_htlc = *effect->u.htlc_in_progress;
/* We can abandon and add a new one, if we're LOWPRIO */ break;
if (effect->htlc_abandon) { case STATE_EFFECT_update_theirsig:
break;
case STATE_EFFECT_htlc_abandon:
if (sdata->current_htlc.htlc.id == -1) if (sdata->current_htlc.htlc.id == -1)
return "HTLC not in progress, can't abandon"; return "HTLC not in progress, can't abandon";
if (effect->htlc_fulfill)
return "Complete and abandon?";
sdata->current_htlc.htlc.id = -1; sdata->current_htlc.htlc.id = -1;
} break;
if (effect->htlc_in_progress) { case STATE_EFFECT_htlc_fulfill:
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) {
if (sdata->current_htlc.htlc.id == -1) if (sdata->current_htlc.htlc.id == -1)
return "HTLC not in progress, can't complete"; return "HTLC not in progress, can't complete";
if (sdata->current_htlc.adding) { 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->htlcs_to_them,
&sdata->num_htlcs_to_them, &sdata->num_htlcs_to_them,
ARRAY_SIZE(sdata->htlcs_to_us), ARRAY_SIZE(sdata->htlcs_to_us),
&sdata->current_htlc.htlc); &sdata->current_htlc.htlc);
} else { } else {
const struct htlc *h; const struct htlc *h;
h = find_htlc(sdata, sdata->current_htlc.htlc.id); h = find_htlc(sdata,
sdata->current_htlc.htlc.id);
if (!h) if (!h)
return "Removing nonexistent HTLC?"; 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"; return "Removing disagreed about to_them";
remove_htlc(sdata->htlcs_to_us, &sdata->num_htlcs_to_us, remove_htlc(sdata->htlcs_to_us, &sdata->num_htlcs_to_us,
sdata->htlcs_to_them, sdata->htlcs_to_them,
@ -1526,81 +1602,46 @@ static const char *apply_effects(struct state_data *sdata,
h); h);
} }
sdata->current_htlc.htlc.id = -1; sdata->current_htlc.htlc.id = -1;
} break;
case STATE_EFFECT_r_value:
if (effect->r_value) { /* We set r_value when they spend an HTLC, so
/* We set r_value when they spend an HTLC, so we can set this * we can set this multiple times (multiple commit
* multiple times (multiple commit txs) */ * txs) */
if (!rval_known(sdata, effect->r_value->id)) { if (!rval_known(sdata, effect->u.r_value->id)) {
if (sdata->num_rvals_known if (sdata->num_rvals_known
== ARRAY_SIZE(sdata->rvals_known)) == ARRAY_SIZE(sdata->rvals_known))
return "Too many rvals"; return "Too many rvals";
sdata->rvals_known[sdata->num_rvals_known++] sdata->rvals_known[sdata->num_rvals_known++]
= effect->r_value->id; = effect->u.r_value->id;
} }
} break;
if (effect->watch_htlcs) { case STATE_EFFECT_watch_htlcs:
assert(sdata->num_live_htlcs_to_us 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)); <= ARRAY_SIZE(sdata->live_htlcs_to_us));
assert(sdata->num_live_htlcs_to_them 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)); <= ARRAY_SIZE(sdata->live_htlcs_to_them));
memcpy(sdata->live_htlcs_to_us + sdata->num_live_htlcs_to_us, memcpy(sdata->live_htlcs_to_us + sdata->num_live_htlcs_to_us,
effect->watch_htlcs->htlcs_to_us, effect->u.watch_htlcs->htlcs_to_us,
effect->watch_htlcs->num_htlcs_to_us effect->u.watch_htlcs->num_htlcs_to_us
* sizeof(effect->watch_htlcs->htlcs_to_us[0])); * sizeof(effect->u.watch_htlcs->htlcs_to_us[0]));
memcpy(sdata->live_htlcs_to_them + sdata->num_live_htlcs_to_them, memcpy(sdata->live_htlcs_to_them + sdata->num_live_htlcs_to_them,
effect->watch_htlcs->htlcs_to_them, effect->u.watch_htlcs->htlcs_to_them,
effect->watch_htlcs->num_htlcs_to_them effect->u.watch_htlcs->num_htlcs_to_them
* sizeof(effect->watch_htlcs->htlcs_to_them[0])); * sizeof(effect->u.watch_htlcs->htlcs_to_them[0]));
sdata->num_live_htlcs_to_us 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 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 */ /* Can happen if we were finished, then new commit tx */
remove_event_(&sdata->core.event_notifies, INPUT_NO_MORE_HTLCS); remove_event_(&sdata->core.event_notifies, INPUT_NO_MORE_HTLCS);
} break;
if (effect->watch_htlc_spend) { case STATE_EFFECT_unwatch_htlc:
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) {
/* Unwatch all? */ /* Unwatch all? */
if (effect->unwatch_htlc->id == -1) { if (effect->u.unwatch_htlc->id == -1) {
/* This can happen if we get in front of /* This can happen if we get in front of
* INPUT_NO_MORE_HTLCS */ * INPUT_NO_MORE_HTLCS */
if (!outstanding_htlc_watches(sdata) if (!outstanding_htlc_watches(sdata)
@ -1614,7 +1655,7 @@ static const char *apply_effects(struct state_data *sdata,
} else { } else {
const struct htlc *h; 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 /* That can fail, when we see them spend (and
* thus stop watching) after we've timed out, * 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 that was last, fire INPUT_NO_MORE_HTLCS */
if (!outstanding_htlc_watches(sdata)) { if (!outstanding_htlc_watches(sdata)) {
assert(effect->unwatch_htlc->all_done assert(effect->u.unwatch_htlc->all_done
== INPUT_NO_MORE_HTLCS); == INPUT_NO_MORE_HTLCS);
add_event(&sdata->core.event_notifies, 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; 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) static void eliminate_input(enum state_input **inputs, enum state_input in)
{ {
size_t i, n = tal_count(*inputs); 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; || 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, static void try_input(const struct state_data *sdata,
enum state_input i, enum state_input i,
const union input *idata, const union input *idata,
@ -1829,11 +1946,12 @@ static void try_input(const struct state_data *sdata,
{ {
struct state_data copy, peer; struct state_data copy, peer;
struct trail t; struct trail t;
struct state_effect *effect = tal(NULL, struct state_effect);
enum state newstate; enum state newstate;
struct state_effect *effect;
const char *problem; const char *problem;
Pkt *output;
const tal_t *ctx = tal(NULL, char);
state_effect_init(effect);
copy_peers(&copy, &peer, sdata); copy_peers(&copy, &peer, sdata);
copy.current_input = i; copy.current_input = i;
@ -1842,7 +1960,9 @@ static void try_input(const struct state_data *sdata,
copy.trail = &t; copy.trail = &t;
eliminate_input(&hist->inputs_per_state[copy.core.state], i); eliminate_input(&hist->inputs_per_state[copy.core.state], i);
newstate = state(copy.core.state, &copy, i, idata, effect); effect = state(ctx, copy.core.state, &copy, i, idata);
newstate = get_state_effect(effect, sdata->core.state);
normalpath &= normal_path(i, sdata->core.state, newstate); normalpath &= normal_path(i, sdata->core.state, newstate);
errorpath |= error_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; newstr = state_name(newstate) + 6;
} }
if (newstr != oldstr || include_nops) 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_all_effects(&copy, effect, &output);
update_trail(&t, &copy, output);
problem = apply_effects(&copy, effect);
update_trail(&t, &copy, effect);
if (problem) if (problem)
report_trail(&t, problem); report_trail(&t, problem);
@ -1879,14 +1998,14 @@ static void try_input(const struct state_data *sdata,
/* Record any output. */ /* Record any output. */
if (effect->send) { if (output) {
record_output(&hist->outputs, record_output(&hist->outputs,
input_by_name((const char *)effect->send)); input_by_name((const char *)output));
} }
if (hist->state_dump) { if (hist->state_dump) {
record_state(&hist->state_dump[sdata->core.state], i, newstate, 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? */ /* 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. * 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_LOWPRIO
|| newstate == STATE_NORMAL_HIGHPRIO || newstate == STATE_NORMAL_HIGHPRIO
|| i == BITCOIN_ANCHOR_OTHERSPEND || i == BITCOIN_ANCHOR_OTHERSPEND
|| i == BITCOIN_ANCHOR_THEIRSPEND || i == BITCOIN_ANCHOR_THEIRSPEND
|| quick) { || quick) {
tal_free(effect); tal_free(ctx);
return; return;
} }
if (t.depth > STATE_MAX * 10) 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. */ /* Don't continue if we reached a different error state. */
if (state_is_error(newstate)) { if (state_is_error(newstate)) {
tal_free(effect); tal_free(ctx);
return; return;
} }
@ -1942,7 +2062,7 @@ static void try_input(const struct state_data *sdata,
if (outstanding_htlc_watches(&copy)) if (outstanding_htlc_watches(&copy))
report_trail(&t, "CLOSED but watching HTLCs?"); report_trail(&t, "CLOSED but watching HTLCs?");
tal_free(effect); tal_free(ctx);
return; return;
} }
@ -1952,7 +2072,7 @@ static void try_input(const struct state_data *sdata,
/* Don't bother running other peer we can't communicate. */ /* Don't bother running other peer we can't communicate. */
if (copy.core.pkt_inputs || peer.core.pkt_inputs) if (copy.core.pkt_inputs || peer.core.pkt_inputs)
run_peer(&peer, normalpath, errorpath, &t, hist); run_peer(&peer, normalpath, errorpath, &t, hist);
tal_free(effect); tal_free(ctx);
} }
static void sanity_check(const struct state_data *sdata) 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); enum state_input **inps = tal_arr(NULL, enum state_input *, STATE_MAX);
unsigned int i; 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++) { for (i = 0; i < STATE_MAX; i++) {
/* This is a global */ /* This is a global */
mapping_inputs = tal_arr(inps, enum state_input, 0); mapping_inputs = tal_arr(inps, enum state_input, 0);
state_effect_init(effect);
/* This adds to mapping_inputs every input_is() call */ /* This adds to mapping_inputs every input_is() call */
if (!state_is_error(i)) if (!state_is_error(i))
state(i, NULL, INPUT_NONE, NULL, effect); state(ctx, i, NULL, INPUT_NONE, NULL);
inps[i] = mapping_inputs; inps[i] = mapping_inputs;
} }
/* Reset global */ /* Reset global */
mapping_inputs = NULL; mapping_inputs = NULL;
tal_free(effect); tal_free(ctx);
return inps; return inps;
} }

Loading…
Cancel
Save