Browse Source

test/test_state_coverage: cut memory usage dramatically.

For loop detection, we don't need entire state.  So extract a core,
which we can put in hash table.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 10 years ago
parent
commit
5a4d07b921
  1. 360
      test/test_state_coverage.c

360
test/test_state_coverage.c

@ -47,17 +47,39 @@ struct htlc_spend_watch {
/* But we can have many different malleated commit txs. */ /* But we can have many different malleated commit txs. */
#define HTLC_ARRSIZE 20 #define HTLC_ARRSIZE 20
struct state_data { /* No padding, for fast compare and hashing. */
struct core_state {
/* What bitcoin/timeout notifications are we subscribed to? */
uint64_t event_notifies;
enum state state; enum state state;
size_t num_outputs; enum state deferred_state;
enum state_input outputs[6];
/* To store HTLC numbers. */
unsigned int pkt_data[6];
enum state_input current_command; enum state_input current_command;
enum state_input deferred_pkt; enum state_input deferred_pkt;
enum state deferred_state;
enum state_input outputs[4];
uint8_t num_outputs;
bool pkt_inputs; bool pkt_inputs;
bool cmd_inputs; bool cmd_inputs;
/* Here down need to be generated from other fields */
bool has_current_htlc;
uint8_t capped_htlcs_to_them;
uint8_t capped_htlcs_to_us;
uint8_t capped_htlc_spends_to_them;
uint8_t capped_htlc_spends_to_us;
uint8_t capped_live_htlcs_to_them;
uint8_t capped_live_htlcs_to_us;
uint8_t pad[6];
};
struct state_data {
struct core_state core;
/* To store HTLC numbers. */
unsigned int pkt_data[4];
/* id == -1 if none currently. */ /* id == -1 if none currently. */
struct htlc_progress current_htlc; struct htlc_progress current_htlc;
@ -77,8 +99,6 @@ struct state_data {
const char *error; const char *error;
/* What bitcoin/timeout notifications are we subscribed to? */
uint64_t event_notifies;
/* ID. */ /* ID. */
const char *name; const char *name;
/* The other peer's sdata. */ /* The other peer's sdata. */
@ -97,7 +117,10 @@ struct trail {
}; };
struct situation { struct situation {
struct state_data a, b; union {
struct core_state s;
uint32_t u32[sizeof(struct core_state)/sizeof(uint32_t)];
} a, b;
}; };
static const struct situation *situation_keyof(const struct situation *situation) static const struct situation *situation_keyof(const struct situation *situation)
@ -105,68 +128,40 @@ static const struct situation *situation_keyof(const struct situation *situation
return situation; return situation;
} }
static uint32_t hash_add(uint64_t val, uint32_t base)
{
return hash_any(&val, sizeof(val), base);
}
/* After 2, we stop looping. */ /* After 2, we stop looping. */
static unsigned int cap(unsigned int val) static unsigned int cap(unsigned int val)
{ {
return val > MAX_HTLCS ? MAX_HTLCS : val; return val > MAX_HTLCS ? MAX_HTLCS : val;
} }
static size_t sdata_hash(const struct state_data *sdata)
{
size_t h;
h = hash(sdata->outputs, sdata->num_outputs, sdata->state);
h = hash_add(sdata->current_command, h);
h = hash_add(sdata->deferred_pkt, h);
h = hash_add(sdata->deferred_state, h);
h = hash_add(sdata->pkt_inputs, h);
h = hash_add(sdata->event_notifies, h);
h = hash_add(cap(sdata->num_htlcs_to_us), h);
h = hash_add(cap(sdata->num_htlcs_to_them), h);
h = hash_add(cap(sdata->num_live_htlcs_to_us), h);
h = hash_add(cap(sdata->num_live_htlcs_to_them), h);
h = hash_add(cap(sdata->num_htlc_spends_to_us), h);
h = hash_add(cap(sdata->num_htlc_spends_to_them), h);
return h;
}
static bool sdata_eq(const struct state_data *a, const struct state_data *b)
{
assert(streq(a->name, b->name));
return a->state == b->state
&& a->num_outputs == b->num_outputs
&& memcmp(a->outputs, b->outputs,
a->num_outputs * sizeof(*a->outputs)) == 0
&& a->current_command == b->current_command
&& a->deferred_pkt == b->deferred_pkt
&& a->deferred_state == b->deferred_state
&& a->pkt_inputs == b->pkt_inputs
&& a->cmd_inputs == b->cmd_inputs
&& a->event_notifies == b->event_notifies
&& ((a->current_htlc.htlc.id == -1)
== (b->current_htlc.htlc.id == -1))
&& cap(a->num_htlcs_to_us) == cap(b->num_htlcs_to_us)
&& cap(a->num_htlcs_to_them) == cap(b->num_htlcs_to_them)
&& cap(a->num_live_htlcs_to_us) == cap(b->num_live_htlcs_to_us)
&& cap(a->num_live_htlcs_to_them) == cap(b->num_live_htlcs_to_them)
&& cap(a->num_htlc_spends_to_us) == cap(b->num_htlc_spends_to_us)
&& cap(a->num_htlc_spends_to_them) == cap(b->num_htlc_spends_to_them);
}
static size_t situation_hash(const struct situation *situation) static size_t situation_hash(const struct situation *situation)
{ {
return hash_add(sdata_hash(&situation->a), sdata_hash(&situation->b)); BUILD_ASSERT(sizeof(situation->a.u32) == sizeof(situation->a.s));
return hash(situation->a.u32, ARRAY_SIZE(situation->a.u32), 0);
} }
static bool situation_eq(const struct situation *a, const struct situation *b) static bool situation_eq(const struct situation *a, const struct situation *b)
{ {
return sdata_eq(&a->a, &b->a) && sdata_eq(&a->b, &b->b); /* No padding */
BUILD_ASSERT(sizeof(a->a.s)
== (sizeof(a->a.s.event_notifies)
+ sizeof(a->a.s.state)
+ sizeof(a->a.s.deferred_state)
+ sizeof(a->a.s.current_command)
+ sizeof(a->a.s.deferred_pkt)
+ sizeof(a->a.s.outputs)
+ sizeof(a->a.s.num_outputs)
+ sizeof(a->a.s.pkt_inputs)
+ sizeof(a->a.s.cmd_inputs)
+ sizeof(a->a.s.has_current_htlc)
+ sizeof(a->a.s.capped_htlcs_to_us)
+ sizeof(a->a.s.capped_htlcs_to_them)
+ sizeof(a->a.s.capped_htlc_spends_to_us)
+ sizeof(a->a.s.capped_htlc_spends_to_them)
+ sizeof(a->a.s.capped_live_htlcs_to_us)
+ sizeof(a->a.s.capped_live_htlcs_to_them)
+ sizeof(a->a.s.pad)));
return structeq(&a->a.s, &b->a.s) && structeq(&a->b.s, &b->b.s);
} }
struct dot_edge { struct dot_edge {
@ -917,8 +912,8 @@ static void sdata_init(struct state_data *sdata,
enum state_input initstate, enum state_input initstate,
const char *name) const char *name)
{ {
sdata->state = initstate; sdata->core.state = initstate;
sdata->num_outputs = 1; sdata->core.num_outputs = 1;
sdata->current_htlc.htlc.id = -1; sdata->current_htlc.htlc.id = -1;
sdata->num_htlcs_to_us = 0; sdata->num_htlcs_to_us = 0;
sdata->num_htlcs_to_them = 0; sdata->num_htlcs_to_them = 0;
@ -927,16 +922,17 @@ static void sdata_init(struct state_data *sdata,
sdata->num_htlc_spends_to_us = 0; sdata->num_htlc_spends_to_us = 0;
sdata->num_htlc_spends_to_them = 0; sdata->num_htlc_spends_to_them = 0;
sdata->num_rvals_known = 0; sdata->num_rvals_known = 0;
memset(sdata->core.pad, 0, sizeof(sdata->core.pad));
sdata->error = NULL; sdata->error = NULL;
memset(sdata->outputs, 0, sizeof(sdata->outputs)); memset(sdata->core.outputs, 0, sizeof(sdata->core.outputs));
sdata->deferred_pkt = INPUT_NONE; sdata->core.deferred_pkt = INPUT_NONE;
sdata->deferred_state = STATE_MAX; sdata->core.deferred_state = STATE_MAX;
sdata->outputs[0] = INPUT_NONE; sdata->core.outputs[0] = INPUT_NONE;
sdata->pkt_data[0] = -1; sdata->pkt_data[0] = -1;
sdata->current_command = INPUT_NONE; sdata->core.current_command = INPUT_NONE;
sdata->event_notifies = 0; sdata->core.event_notifies = 0;
sdata->pkt_inputs = true; sdata->core.pkt_inputs = true;
sdata->cmd_inputs = true; sdata->core.cmd_inputs = true;
sdata->name = name; sdata->name = name;
sdata->peer = other; sdata->peer = other;
} }
@ -956,6 +952,17 @@ static struct trail *run_peer(const struct state_data *sdata,
size_t depth, size_t depth,
struct hist *hist); struct hist *hist);
static void update_core(struct core_state *core, const struct state_data *sdata)
{
core->has_current_htlc = sdata->current_htlc.htlc.id != -1;
core->capped_htlcs_to_us = cap(sdata->num_htlcs_to_us);
core->capped_htlcs_to_them = cap(sdata->num_htlcs_to_them);
core->capped_live_htlcs_to_us = cap(sdata->num_live_htlcs_to_us);
core->capped_live_htlcs_to_them = cap(sdata->num_live_htlcs_to_them);
core->capped_htlc_spends_to_us = cap(sdata->num_htlc_spends_to_us);
core->capped_htlc_spends_to_them = cap(sdata->num_htlc_spends_to_them);
}
/* Returns false if we've been here before. */ /* Returns false if we've been here before. */
static bool sithash_update(struct sithash *sithash, static bool sithash_update(struct sithash *sithash,
const struct state_data *sdata) const struct state_data *sdata)
@ -963,11 +970,15 @@ static bool sithash_update(struct sithash *sithash,
struct situation sit; struct situation sit;
if (streq(sdata->name, "A")) { if (streq(sdata->name, "A")) {
sit.a = *sdata; sit.a.s = sdata->core;
sit.b = *sdata->peer; sit.b.s = sdata->peer->core;
update_core(&sit.a.s, sdata);
update_core(&sit.b.s, sdata->peer);
} else { } else {
sit.b = *sdata; sit.b.s = sdata->core;
sit.a = *sdata->peer; sit.a.s = sdata->peer->core;
update_core(&sit.b.s, sdata);
update_core(&sit.a.s, sdata->peer);
} }
if (sithash_get(sithash, &sit)) if (sithash_get(sithash, &sit))
@ -1028,7 +1039,7 @@ static bool is_current_command(const struct state_data *sdata,
|| is_current_command(sdata, CMD_SEND_HTLC_TIMEDOUT) || is_current_command(sdata, CMD_SEND_HTLC_TIMEDOUT)
|| is_current_command(sdata, CMD_SEND_HTLC_ROUTEFAIL); || is_current_command(sdata, CMD_SEND_HTLC_ROUTEFAIL);
} }
return sdata->current_command == cmd; return sdata->core.current_command == cmd;
} }
static void add_htlc(struct htlc *to_us, unsigned int *num_to_us, static void add_htlc(struct htlc *to_us, unsigned int *num_to_us,
@ -1109,79 +1120,81 @@ static const char *apply_effects(struct state_data *sdata,
return pkt; return pkt;
} }
} }
assert(sdata->num_outputs < ARRAY_SIZE(sdata->outputs)); assert(sdata->core.num_outputs <ARRAY_SIZE(sdata->core.outputs));
sdata->outputs[sdata->num_outputs] = input_by_name(pkt); sdata->core.outputs[sdata->core.num_outputs]
sdata->pkt_data[sdata->num_outputs++] = input_by_name(pkt);
sdata->pkt_data[sdata->core.num_outputs++]
= htlc_id_from_pkt(effect->send); = htlc_id_from_pkt(effect->send);
} }
if (effect->watch) { if (effect->watch) {
/* We can have multiple steals or spendtheirs in flight, /* We can have multiple steals or spendtheirs in flight,
so make exceptions for BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */ so make exceptions for BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */
if (sdata->event_notifies & (1ULL << BITCOIN_STEAL_DONE) if (sdata->core.event_notifies & (1ULL << BITCOIN_STEAL_DONE)
& effect->watch->events) & effect->watch->events)
remove_event(&effect->watch->events, BITCOIN_STEAL_DONE); remove_event(&effect->watch->events, BITCOIN_STEAL_DONE);
if (sdata->event_notifies & (1ULL << BITCOIN_SPEND_THEIRS_DONE) if (sdata->core.event_notifies
& (1ULL << BITCOIN_SPEND_THEIRS_DONE)
& effect->watch->events) & effect->watch->events)
remove_event(&effect->watch->events, remove_event(&effect->watch->events,
BITCOIN_SPEND_THEIRS_DONE); BITCOIN_SPEND_THEIRS_DONE);
if (sdata->event_notifies & effect->watch->events) if (sdata->core.event_notifies & effect->watch->events)
return "event set twice"; return "event set twice";
sdata->event_notifies |= effect->watch->events; sdata->core.event_notifies |= effect->watch->events;
} }
if (effect->unwatch) { if (effect->unwatch) {
if ((sdata->event_notifies & effect->unwatch->events) if ((sdata->core.event_notifies & effect->unwatch->events)
!= effect->unwatch->events) != effect->unwatch->events)
return "unset event unwatched"; return "unset event unwatched";
sdata->event_notifies &= ~effect->unwatch->events; sdata->core.event_notifies &= ~effect->unwatch->events;
} }
if (effect->defer != INPUT_NONE) { if (effect->defer != INPUT_NONE) {
/* 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->defer))
sdata->current_command = INPUT_NONE; sdata->core.current_command = INPUT_NONE;
else if (input_is_pkt(effect->defer)) { else if (input_is_pkt(effect->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->deferred_pkt == INPUT_NONE assert(sdata->core.deferred_pkt == INPUT_NONE
|| sdata->deferred_pkt == effect->defer); || sdata->core.deferred_pkt == effect->defer);
sdata->deferred_pkt = effect->defer; sdata->core.deferred_pkt = effect->defer;
sdata->deferred_state = sdata->state; sdata->core.deferred_state = sdata->core.state;
} }
} }
if (effect->complete != INPUT_NONE) { if (effect->complete != INPUT_NONE) {
if (!is_current_command(sdata, effect->complete)) if (!is_current_command(sdata, effect->complete))
return tal_fmt(NULL, "Completed %s not %s", return tal_fmt(NULL, "Completed %s not %s",
input_name(effect->complete), input_name(effect->complete),
input_name(sdata->current_command)); input_name(sdata->core.current_command));
sdata->current_command = INPUT_NONE; sdata->core.current_command = INPUT_NONE;
} }
if (effect->stop_packets) { if (effect->stop_packets) {
if (!sdata->pkt_inputs) if (!sdata->core.pkt_inputs)
return "stop_packets twice"; return "stop_packets twice";
sdata->pkt_inputs = false; sdata->core.pkt_inputs = false;
/* Can no longer receive packet timeouts, either. */ /* Can no longer receive packet timeouts, either. */
remove_event_(&sdata->event_notifies, remove_event_(&sdata->core.event_notifies,
INPUT_CLOSE_COMPLETE_TIMEOUT); INPUT_CLOSE_COMPLETE_TIMEOUT);
} }
if (effect->stop_commands) { if (effect->stop_commands) {
if (!sdata->cmd_inputs) if (!sdata->core.cmd_inputs)
return "stop_commands twice"; return "stop_commands twice";
if (sdata->current_command != INPUT_NONE) if (sdata->core.current_command != INPUT_NONE)
return tal_fmt(NULL, "stop_commands with pending command %s", return tal_fmt(NULL, "stop_commands with pending command %s",
input_name(sdata->current_command)); input_name(sdata->core.current_command));
sdata->cmd_inputs = false; sdata->core.cmd_inputs = false;
} }
if (effect->close_timeout != INPUT_NONE) { if (effect->close_timeout != INPUT_NONE) {
add_event(&sdata->event_notifies, effect->close_timeout); add_event(&sdata->core.event_notifies, effect->close_timeout);
/* We assume this. */ /* We assume this. */
assert(effect->close_timeout == INPUT_CLOSE_COMPLETE_TIMEOUT); assert(effect->close_timeout == INPUT_CLOSE_COMPLETE_TIMEOUT);
} }
if (effect->in_error) { if (effect->in_error) {
/* We should stop talking to them after error received. */ /* We should stop talking to them after error received. */
if (sdata->pkt_inputs) if (sdata->core.pkt_inputs)
return "packets still open after error pkt"; return "packets still open after error pkt";
} }
/* We can abandon and add a new one, if we're LOWPRIO */ /* We can abandon and add a new one, if we're LOWPRIO */
@ -1258,7 +1271,7 @@ static const char *apply_effects(struct state_data *sdata,
sdata->num_live_htlcs_to_them sdata->num_live_htlcs_to_them
+= effect->watch_htlcs->num_htlcs_to_them; += effect->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->event_notifies, INPUT_NO_MORE_HTLCS); remove_event_(&sdata->core.event_notifies, INPUT_NO_MORE_HTLCS);
} }
if (effect->watch_htlc_spend) { if (effect->watch_htlc_spend) {
const struct htlc *h; const struct htlc *h;
@ -1291,7 +1304,7 @@ static const char *apply_effects(struct state_data *sdata,
if (!outstanding_htlc_watches(sdata)) { if (!outstanding_htlc_watches(sdata)) {
assert(effect->unwatch_htlc_spend->done assert(effect->unwatch_htlc_spend->done
== INPUT_NO_MORE_HTLCS); == INPUT_NO_MORE_HTLCS);
add_event(&sdata->event_notifies, add_event(&sdata->core.event_notifies,
effect->unwatch_htlc_spend->done); effect->unwatch_htlc_spend->done);
} }
} }
@ -1301,7 +1314,7 @@ static const char *apply_effects(struct state_data *sdata,
/* 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)
&& !have_event(sdata->event_notifies, && !have_event(sdata->core.event_notifies,
INPUT_NO_MORE_HTLCS)) INPUT_NO_MORE_HTLCS))
return "unwatching all with no htlcs?"; return "unwatching all with no htlcs?";
sdata->num_htlc_spends_to_us = 0; sdata->num_htlc_spends_to_us = 0;
@ -1328,7 +1341,7 @@ static const char *apply_effects(struct state_data *sdata,
if (!outstanding_htlc_watches(sdata)) { if (!outstanding_htlc_watches(sdata)) {
assert(effect->unwatch_htlc->all_done assert(effect->unwatch_htlc->all_done
== INPUT_NO_MORE_HTLCS); == INPUT_NO_MORE_HTLCS);
add_event(&sdata->event_notifies, add_event(&sdata->core.event_notifies,
effect->unwatch_htlc->all_done); effect->unwatch_htlc->all_done);
} }
} }
@ -1513,8 +1526,8 @@ static bool waiting_statepair(enum state a, enum state b)
static bool has_packets(const struct state_data *sdata) static bool has_packets(const struct state_data *sdata)
{ {
return sdata->deferred_pkt != INPUT_NONE return sdata->core.deferred_pkt != INPUT_NONE
|| sdata->num_outputs != 0; || sdata->core.num_outputs != 0;
} }
static struct trail *try_input(const struct state_data *sdata, static struct trail *try_input(const struct state_data *sdata,
@ -1532,24 +1545,24 @@ static struct trail *try_input(const struct state_data *sdata,
state_effect_init(effect); state_effect_init(effect);
eliminate_input(&hist->inputs_per_state[sdata->state], i); eliminate_input(&hist->inputs_per_state[sdata->core.state], i);
newstate = state(sdata->state, sdata, i, idata, effect); newstate = state(sdata->core.state, sdata, i, idata, effect);
normalpath &= normal_path(i, sdata->state, newstate); normalpath &= normal_path(i, sdata->core.state, newstate);
errorpath |= error_path(i, sdata->state, newstate); errorpath |= error_path(i, sdata->core.state, newstate);
if (dot_enable if (dot_enable
&& (dot_include_abnormal || normalpath) && (dot_include_abnormal || normalpath)
&& (dot_include_errors || !errorpath) && (dot_include_errors || !errorpath)
&& (dot_include_abnormal || !too_cluttered(i, sdata->state))) { && (dot_include_abnormal || !too_cluttered(i, sdata->core.state))) {
const char *oldstr, *newstr; const char *oldstr, *newstr;
/* Simplify folds high and low prio, skip "STATE_" */ /* Simplify folds high and low prio, skip "STATE_" */
if (dot_simplify) { if (dot_simplify) {
oldstr = simplify_state(sdata->state) + 6; oldstr = simplify_state(sdata->core.state) + 6;
newstr = simplify_state(newstate) + 6; newstr = simplify_state(newstate) + 6;
} else { } else {
oldstr = state_name(sdata->state) + 6; oldstr = state_name(sdata->core.state) + 6;
newstr = state_name(newstate) + 6; newstr = state_name(newstate) + 6;
} }
if (newstr != oldstr || include_nops) if (newstr != oldstr || include_nops)
@ -1557,7 +1570,7 @@ static struct trail *try_input(const struct state_data *sdata,
} }
copy_peers(&copy, &peer, sdata); copy_peers(&copy, &peer, sdata);
copy.state = newstate; copy.core.state = newstate;
if (newstate == STATE_ERR_INTERNAL) if (newstate == STATE_ERR_INTERNAL)
return new_trail(i, idata, sdata, &copy, effect, return new_trail(i, idata, sdata, &copy, effect,
@ -1576,7 +1589,7 @@ static struct trail *try_input(const struct state_data *sdata,
} }
if (hist->state_dump) { if (hist->state_dump) {
record_state(&hist->state_dump[sdata->state], i, newstate, record_state(&hist->state_dump[sdata->core.state], i, newstate,
(const char *)effect->send); (const char *)effect->send);
} }
@ -1611,24 +1624,24 @@ static struct trail *try_input(const struct state_data *sdata,
/* /*
* If we're listening, someone should be talking (usually). * If we're listening, someone should be talking (usually).
*/ */
if (copy.pkt_inputs && !has_packets(&copy) && !has_packets(&peer) if (copy.core.pkt_inputs && !has_packets(&copy) && !has_packets(&peer)
&& !waiting_statepair(copy.state, peer.state)) { && !waiting_statepair(copy.core.state, peer.core.state)) {
return new_trail(i, idata, sdata, &copy, effect, "Deadlock"); return new_trail(i, idata, sdata, &copy, effect, "Deadlock");
} }
/* Finished? */ /* Finished? */
if (newstate == STATE_CLOSED) { if (newstate == STATE_CLOSED) {
if (copy.pkt_inputs) if (copy.core.pkt_inputs)
return new_trail(i, idata, sdata, &copy, effect, return new_trail(i, idata, sdata, &copy, effect,
"CLOSED but taking packets?"); "CLOSED but taking packets?");
if (copy.cmd_inputs) if (copy.core.cmd_inputs)
return new_trail(i, idata, sdata, &copy, effect, return new_trail(i, idata, sdata, &copy, effect,
"CLOSED but taking commands?"); "CLOSED but taking commands?");
if (copy.current_command != INPUT_NONE) if (copy.core.current_command != INPUT_NONE)
return new_trail(i, idata, sdata, &copy, effect, return new_trail(i, idata, sdata, &copy, effect,
input_name(copy.current_command)); input_name(copy.core.current_command));
if (copy.current_htlc.htlc.id != -1) if (copy.current_htlc.htlc.id != -1)
return new_trail(i, idata, sdata, &copy, effect, return new_trail(i, idata, sdata, &copy, effect,
"CLOSED with htlc in progress?"); "CLOSED with htlc in progress?");
@ -1653,12 +1666,12 @@ static struct trail *try_input(const struct state_data *sdata,
static void sanity_check(const struct state_data *sdata) static void sanity_check(const struct state_data *sdata)
{ {
if (sdata->state == STATE_NORMAL_LOWPRIO if (sdata->core.state == STATE_NORMAL_LOWPRIO
|| sdata->state == STATE_NORMAL_HIGHPRIO) { || sdata->core.state == STATE_NORMAL_HIGHPRIO) {
/* Home state: expect commands to be finished. */ /* Home state: expect commands to be finished. */
if (sdata->current_command != INPUT_NONE) if (sdata->core.current_command != INPUT_NONE)
errx(1, "Unexpected command %u in state %u", errx(1, "Unexpected command %u in state %u",
sdata->current_command, sdata->state); sdata->core.current_command, sdata->core.state);
} }
} }
@ -1668,28 +1681,30 @@ static void activate_event(struct state_data *sdata, enum state_input i)
switch (i) { switch (i) {
case BITCOIN_ANCHOR_DEPTHOK: case BITCOIN_ANCHOR_DEPTHOK:
/* Can't sent TIMEOUT (may not be set) */ /* Can't sent TIMEOUT (may not be set) */
remove_event_(&sdata->event_notifies, BITCOIN_ANCHOR_TIMEOUT); remove_event_(&sdata->core.event_notifies, BITCOIN_ANCHOR_TIMEOUT);
break; break;
case BITCOIN_ANCHOR_TIMEOUT: case BITCOIN_ANCHOR_TIMEOUT:
/* Can't sent DEPTHOK */ /* Can't sent DEPTHOK */
remove_event(&sdata->event_notifies, BITCOIN_ANCHOR_DEPTHOK); remove_event(&sdata->core.event_notifies, BITCOIN_ANCHOR_DEPTHOK);
break; break;
/* And of the "done" cases means we won't give the others. */ /* And of the "done" cases means we won't give the others. */
case BITCOIN_SPEND_THEIRS_DONE: case BITCOIN_SPEND_THEIRS_DONE:
case BITCOIN_SPEND_OURS_DONE: case BITCOIN_SPEND_OURS_DONE:
case BITCOIN_STEAL_DONE: case BITCOIN_STEAL_DONE:
case BITCOIN_CLOSE_DONE: case BITCOIN_CLOSE_DONE:
remove_event_(&sdata->event_notifies, BITCOIN_SPEND_OURS_DONE); remove_event_(&sdata->core.event_notifies,
remove_event_(&sdata->event_notifies, BITCOIN_SPEND_THEIRS_DONE); BITCOIN_SPEND_OURS_DONE);
remove_event_(&sdata->event_notifies, BITCOIN_STEAL_DONE); remove_event_(&sdata->core.event_notifies,
remove_event_(&sdata->event_notifies, BITCOIN_CLOSE_DONE); BITCOIN_SPEND_THEIRS_DONE);
remove_event_(&sdata->event_notifies, remove_event_(&sdata->core.event_notifies, BITCOIN_STEAL_DONE);
remove_event_(&sdata->core.event_notifies, BITCOIN_CLOSE_DONE);
remove_event_(&sdata->core.event_notifies,
BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED); BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED);
remove_event_(&sdata->event_notifies, remove_event_(&sdata->core.event_notifies,
BITCOIN_ANCHOR_THEIRSPEND); BITCOIN_ANCHOR_THEIRSPEND);
remove_event_(&sdata->event_notifies, remove_event_(&sdata->core.event_notifies,
BITCOIN_ANCHOR_OTHERSPEND); BITCOIN_ANCHOR_OTHERSPEND);
remove_event_(&sdata->event_notifies, remove_event_(&sdata->core.event_notifies,
BITCOIN_ANCHOR_UNSPENT); BITCOIN_ANCHOR_UNSPENT);
break; break;
default: default:
@ -1743,45 +1758,45 @@ static struct trail *run_peer(const struct state_data *sdata,
copy_peers(&copy, &peer, sdata); copy_peers(&copy, &peer, sdata);
/* Try the event notifiers */ /* Try the event notifiers */
old_notifies = copy.event_notifies; old_notifies = copy.core.event_notifies;
for (i = 0; i < 64; i++) { for (i = 0; i < 64; i++) {
if (!have_event(copy.event_notifies, i)) if (!have_event(copy.core.event_notifies, i))
continue; continue;
/* Don't re-fire most events */ /* Don't re-fire most events */
if (!can_refire(i)) if (!can_refire(i))
remove_event(&copy.event_notifies, i); remove_event(&copy.core.event_notifies, i);
activate_event(&copy, i); activate_event(&copy, i);
t = try_input(&copy, i, idata, normalpath, errorpath, depth, hist); t = try_input(&copy, i, idata, normalpath, errorpath, depth, hist);
if (t) if (t)
return t; return t;
copy.event_notifies = old_notifies; copy.core.event_notifies = old_notifies;
} }
/* Try sending commands (unless in init state, closed or /* Try sending commands (unless in init state, closed or
* already doing one). */ * already doing one). */
if (sdata->state != STATE_INIT_WITHANCHOR if (sdata->core.state != STATE_INIT_WITHANCHOR
&& sdata->state != STATE_INIT_NOANCHOR && sdata->core.state != STATE_INIT_NOANCHOR
&& sdata->cmd_inputs && sdata->core.cmd_inputs
&& sdata->current_command == INPUT_NONE) { && sdata->core.current_command == INPUT_NONE) {
unsigned int i; unsigned int i;
/* We can always add a new HTLC, or close. */ /* We can always add a new HTLC, or close. */
copy.current_command = CMD_SEND_HTLC_UPDATE; copy.core.current_command = CMD_SEND_HTLC_UPDATE;
idata->htlc_prog = tal(idata, struct htlc_progress); idata->htlc_prog = tal(idata, struct htlc_progress);
idata->htlc_prog->adding = true; idata->htlc_prog->adding = true;
idata->htlc_prog->htlc.to_them = true; idata->htlc_prog->htlc.to_them = true;
idata->htlc_prog->htlc.id = next_htlc_id(); idata->htlc_prog->htlc.id = next_htlc_id();
t = try_input(&copy, copy.current_command, idata, t = try_input(&copy, copy.core.current_command, idata,
normalpath, errorpath, depth, normalpath, errorpath, depth,
hist); hist);
if (t) if (t)
return t; return t;
idata->htlc = tal_free(idata->htlc); idata->htlc = tal_free(idata->htlc);
copy.current_command = CMD_CLOSE; copy.core.current_command = CMD_CLOSE;
t = try_input(&copy, copy.current_command, idata, t = try_input(&copy, copy.core.current_command, idata,
normalpath, errorpath, depth, normalpath, errorpath, depth,
hist); hist);
if (t) if (t)
@ -1795,15 +1810,16 @@ static struct trail *run_peer(const struct state_data *sdata,
/* Only send this once. */ /* Only send this once. */
if (!rval_known(sdata, idata->htlc_prog->htlc.id)) { if (!rval_known(sdata, idata->htlc_prog->htlc.id)) {
copy.current_command = CMD_SEND_HTLC_FULFILL; copy.core.current_command
t = try_input(&copy, copy.current_command, = CMD_SEND_HTLC_FULFILL;
t = try_input(&copy, copy.core.current_command,
idata, normalpath, errorpath, idata, normalpath, errorpath,
depth, hist); depth, hist);
if (t) if (t)
return t; return t;
} }
copy.current_command = CMD_SEND_HTLC_ROUTEFAIL; copy.core.current_command = CMD_SEND_HTLC_ROUTEFAIL;
t = try_input(&copy, copy.current_command, t = try_input(&copy, copy.core.current_command,
idata, normalpath, errorpath, idata, normalpath, errorpath,
depth, hist); depth, hist);
if (t) if (t)
@ -1816,8 +1832,8 @@ static struct trail *run_peer(const struct state_data *sdata,
idata->htlc_prog->htlc = sdata->htlcs_to_them[i]; idata->htlc_prog->htlc = sdata->htlcs_to_them[i];
idata->htlc_prog->adding = false; idata->htlc_prog->adding = false;
copy.current_command = CMD_SEND_HTLC_TIMEDOUT; copy.core.current_command = CMD_SEND_HTLC_TIMEDOUT;
t = try_input(&copy, copy.current_command, t = try_input(&copy, copy.core.current_command,
idata, normalpath, errorpath, idata, normalpath, errorpath,
depth, hist); depth, hist);
if (t) if (t)
@ -1825,7 +1841,7 @@ static struct trail *run_peer(const struct state_data *sdata,
} }
/* Restore current_command */ /* Restore current_command */
copy.current_command = INPUT_NONE; copy.core.current_command = INPUT_NONE;
} }
/* If they're watching HTLCs, we can send events. */ /* If they're watching HTLCs, we can send events. */
@ -1879,14 +1895,14 @@ static struct trail *run_peer(const struct state_data *sdata,
} }
/* Allowed to send inputs? */ /* Allowed to send inputs? */
if (copy.pkt_inputs) { if (copy.core.pkt_inputs) {
enum state_input i; enum state_input i;
if (copy.deferred_pkt != INPUT_NONE) { if (copy.core.deferred_pkt != INPUT_NONE) {
/* Can only resubmit once state changed. */ /* Can only resubmit once state changed. */
if (copy.state != copy.deferred_state) { if (copy.core.state != copy.core.deferred_state) {
i = copy.deferred_pkt; i = copy.core.deferred_pkt;
copy.deferred_pkt = INPUT_NONE; copy.core.deferred_pkt = INPUT_NONE;
return try_input(&copy, i, idata, return try_input(&copy, i, idata,
normalpath, errorpath, depth, normalpath, errorpath, depth,
hist); hist);
@ -1894,9 +1910,9 @@ static struct trail *run_peer(const struct state_data *sdata,
/* Can't send anything until that's done. */ /* Can't send anything until that's done. */
return NULL; return NULL;
} }
if (peer.num_outputs) { if (peer.core.num_outputs) {
i = peer.outputs[0]; i = peer.core.outputs[0];
if (peer.pkt_data[0] == -1U) if (peer.pkt_data[0] == -1U)
idata->pkt = (Pkt *)talz(idata, char); idata->pkt = (Pkt *)talz(idata, char);
else else
@ -1904,11 +1920,12 @@ static struct trail *run_peer(const struct state_data *sdata,
peer.pkt_data[0]); peer.pkt_data[0]);
/* Do the first, recursion does the rest. */ /* Do the first, recursion does the rest. */
memmove(peer.outputs, peer.outputs + 1, memmove(peer.core.outputs, peer.core.outputs + 1,
sizeof(peer.outputs) - sizeof(peer.outputs[0])); sizeof(peer.core.outputs)
- sizeof(peer.core.outputs[0]));
memmove(peer.pkt_data, peer.pkt_data + 1, memmove(peer.pkt_data, peer.pkt_data + 1,
sizeof(peer.pkt_data) - sizeof(peer.pkt_data[0])); sizeof(peer.pkt_data)-sizeof(peer.pkt_data[0]));
peer.num_outputs--; peer.core.num_outputs--;
return try_input(&copy, i, idata, normalpath, errorpath, return try_input(&copy, i, idata, normalpath, errorpath,
depth, hist); depth, hist);
} }
@ -1964,10 +1981,10 @@ static bool visited_state(const struct sithash *sithash,
for (h = sithash_first(sithash, &i); h; h = sithash_next(sithash, &i)) { for (h = sithash_first(sithash, &i); h; h = sithash_next(sithash, &i)) {
num++; num++;
if (b) { if (b) {
if (h->b.state == state) if (h->b.s.state == state)
return true; return true;
} else { } else {
if (h->a.state == state) if (h->a.s.state == state)
return true; return true;
} }
} }
@ -1982,8 +1999,9 @@ static void report_trail(const struct trail *t)
fprintf(stderr, "%s: %s(%i) %s -> %s\n", fprintf(stderr, "%s: %s(%i) %s -> %s\n",
t->name, t->name,
input_name(t->input), t->htlc_id, input_name(t->input), t->htlc_id,
state_name(t->before.state), state_name(t->after.state)); state_name(t->before.core.state),
if (t->after.state >= STATE_CLOSED) { state_name(t->after.core.state));
if (t->after.core.state >= STATE_CLOSED) {
if (t->after.num_live_htlcs_to_us if (t->after.num_live_htlcs_to_us
|| t->after.num_live_htlcs_to_them) { || t->after.num_live_htlcs_to_them) {
fprintf(stderr, " Live HTLCs:"); fprintf(stderr, " Live HTLCs:");
@ -2093,7 +2111,11 @@ int main(int argc, char *argv[])
} else } else
hist.state_dump = NULL; hist.state_dump = NULL;
#if 0
quick = dot_enable || dump_states; quick = dot_enable || dump_states;
#else
quick = true;
#endif
/* Initialize universe. */ /* Initialize universe. */
sdata_init(&a, &b, STATE_INIT_WITHANCHOR, "A"); sdata_init(&a, &b, STATE_INIT_WITHANCHOR, "A");

Loading…
Cancel
Save