Browse Source

state: add callbacks for adding/removing watches.

This lets us eliminate struct state_effect altogether (the next patch
removes the now-unused arguments).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
63ea6bfd7a
  1. 221
      state.c
  2. 190
      state.h
  3. 722
      test/test_state_coverage.c

221
state.c

@ -12,15 +12,6 @@ static inline bool high_priority(enum state state)
#define toggle_prio(state, name) \
(!high_priority(state) ? name##_HIGHPRIO : name##_LOWPRIO)
#define add_effect(e, field, val) \
do { \
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)
/* STATE_CLOSE* can be treated as a bitset offset from STATE_CLOSED */
#define BITS_TO_STATE(bits) (STATE_CLOSED + (bits))
#define STATE_TO_BITS(state) ((state) - STATE_CLOSED)
@ -110,7 +101,6 @@ enum command_status state(const tal_t *ctx,
Pkt *decline;
struct bitcoin_tx *tx;
Pkt *err;
struct htlc_watch *htlcs;
enum command_status cstatus = CMD_NONE;
*out = NULL;
@ -183,13 +173,12 @@ enum command_status state(const tal_t *ctx,
}
queue_pkt(out,
pkt_open_commit_sig(ctx, peer));
add_effect(effect, watch,
bitcoin_watch_anchor(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT,
BITCOIN_ANCHOR_UNSPENT,
BITCOIN_ANCHOR_THEIRSPEND,
BITCOIN_ANCHOR_OTHERSPEND));
peer_watch_anchor(peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT,
BITCOIN_ANCHOR_UNSPENT,
BITCOIN_ANCHOR_THEIRSPEND,
BITCOIN_ANCHOR_OTHERSPEND);
return next_state(peer, cstatus,
STATE_OPEN_WAITING_THEIRANCHOR);
@ -210,13 +199,12 @@ enum command_status state(const tal_t *ctx,
goto err_start_unilateral_close;
}
queue_tx_broadcast(broadcast, bitcoin_anchor(ctx, peer));
add_effect(effect, watch,
bitcoin_watch_anchor(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE,
BITCOIN_ANCHOR_UNSPENT,
BITCOIN_ANCHOR_THEIRSPEND,
BITCOIN_ANCHOR_OTHERSPEND));
peer_watch_anchor(peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE,
BITCOIN_ANCHOR_UNSPENT,
BITCOIN_ANCHOR_THEIRSPEND,
BITCOIN_ANCHOR_OTHERSPEND);
return next_state(peer, cstatus,
STATE_OPEN_WAITING_OURANCHOR);
} else if (input_is(input, CMD_CLOSE)) {
@ -249,10 +237,9 @@ enum command_status state(const tal_t *ctx,
goto anchor_unspent;
} else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto them_unilateral;
} else if (input_is(input, BITCOIN_ANCHOR_OTHERSPEND)) {
@ -260,26 +247,23 @@ enum command_status state(const tal_t *ctx,
return next_state(peer, cstatus, STATE_ERR_INFORMATION_LEAK);
} else if (input_is(input, CMD_CLOSE)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto start_closing;
} else if (input_is(input, PKT_CLOSE)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto accept_closing;
} else if (input_is_pkt(input)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
INPUT_NONE);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto unexpected_pkt_nocleanup;
}
@ -315,34 +299,30 @@ enum command_status state(const tal_t *ctx,
STATE_ERR_INFORMATION_LEAK);
} else if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto them_unilateral;
} else if (input_is(input, CMD_CLOSE)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto start_closing;
} else if (input_is(input, PKT_CLOSE)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto accept_closing;
} else if (input_is_pkt(input)) {
/* We no longer care about anchor depth. */
add_effect(effect, unwatch,
bitcoin_unwatch_anchor_depth(ctx, peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT));
peer_unwatch_anchor_depth(peer,
BITCOIN_ANCHOR_DEPTHOK,
BITCOIN_ANCHOR_TIMEOUT);
complete_cmd(peer, &cstatus, CMD_FAIL);
goto unexpected_pkt;
}
@ -738,8 +718,7 @@ enum command_status state(const tal_t *ctx,
/* One a steal is complete, we don't care about htlcs
* (we stole them all) */
if (bits & STATE_CLOSE_HTLCS_BIT)
add_effect(effect, unwatch_htlc,
htlc_unwatch_all(ctx, peer));
peer_unwatch_all_htlc_outputs(peer);
return next_state(peer, cstatus, STATE_CLOSED);
}
@ -764,9 +743,7 @@ enum command_status state(const tal_t *ctx,
tx = bitcoin_spend_ours(ctx, peer);
/* Now we need to wait for our commit to be done. */
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch(ctx, tx,
BITCOIN_SPEND_OURS_DONE));
peer_watch_tx(peer, tx, BITCOIN_SPEND_OURS_DONE);
bits &= ~STATE_CLOSE_OURCOMMIT_BIT;
bits |= STATE_CLOSE_SPENDOURS_BIT;
return next_state(peer, cstatus, BITS_TO_STATE(bits));
@ -791,9 +768,8 @@ enum command_status state(const tal_t *ctx,
/* They revealed R value. */
peer_tx_revealed_r_value(peer, idata->btc);
/* We don't care any more. */
add_effect(effect, unwatch_htlc,
htlc_unwatch(ctx, idata->htlc,
INPUT_NO_MORE_HTLCS));
peer_unwatch_htlc_output(peer, idata->htlc,
INPUT_NO_MORE_HTLCS);
return unchanged_state(cstatus);
} else if (input_is(input, BITCOIN_HTLC_TOTHEM_TIMEOUT)){
tx = bitcoin_htlc_timeout(ctx,
@ -805,11 +781,8 @@ enum command_status state(const tal_t *ctx,
* try to spend, revealing rvalue. */
/* We're done when that gets buried. */
add_effect(effect, watch_htlc_spend,
htlc_spend_watch(ctx,
tx,
idata->cmd,
BITCOIN_HTLC_RETURN_SPEND_DONE));
peer_watch_htlc_spend(peer, tx, idata->htlc,
BITCOIN_HTLC_RETURN_SPEND_DONE);
return unchanged_state(cstatus);
} else if (input_is(input, INPUT_RVALUE)) {
tx = bitcoin_htlc_spend(ctx, peer,
@ -818,44 +791,34 @@ enum command_status state(const tal_t *ctx,
/* Spend it... */
queue_tx_broadcast(broadcast, tx);
/* We're done when it gets buried. */
add_effect(effect, watch_htlc_spend,
htlc_spend_watch(ctx,
tx,
idata->cmd,
BITCOIN_HTLC_FULFILL_SPEND_DONE));
peer_watch_htlc_spend(peer, tx, idata->htlc,
BITCOIN_HTLC_FULFILL_SPEND_DONE);
/* Don't care about this one any more. */
add_effect(effect, unwatch_htlc,
htlc_unwatch(ctx, idata->htlc,
INPUT_NO_MORE_HTLCS));
peer_unwatch_htlc_output(peer, idata->htlc,
INPUT_NO_MORE_HTLCS);
return unchanged_state(cstatus);
} else if (input_is(input, BITCOIN_HTLC_FULFILL_SPEND_DONE)) {
/* Stop watching spend, send
* INPUT_NO_MORE_HTLCS when done. */
add_effect(effect, unwatch_htlc_spend,
htlc_spend_unwatch(ctx,
idata->htlc,
INPUT_NO_MORE_HTLCS));
peer_unwatch_htlc_spend(peer, idata->htlc,
INPUT_NO_MORE_HTLCS);
return unchanged_state(cstatus);
} else if (input_is(input, BITCOIN_HTLC_RETURN_SPEND_DONE)) {
/* Stop watching spend, send
* INPUT_NO_MORE_HTLCS when done. */
add_effect(effect, unwatch_htlc_spend,
htlc_spend_unwatch(ctx,
idata->htlc,
INPUT_NO_MORE_HTLCS));
peer_unwatch_htlc_spend(peer, idata->htlc,
INPUT_NO_MORE_HTLCS);
/* Don't need to watch the HTLC output any more,
* either. */
add_effect(effect, unwatch_htlc,
htlc_unwatch(ctx, idata->htlc,
INPUT_NO_MORE_HTLCS));
peer_unwatch_htlc_output(peer, idata->htlc,
INPUT_NO_MORE_HTLCS);
return unchanged_state(cstatus);
} else if (input_is(input, BITCOIN_HTLC_TOUS_TIMEOUT)) {
/* They can spend, we no longer care
* about this HTLC. */
add_effect(effect, unwatch_htlc,
htlc_unwatch(ctx, idata->htlc,
INPUT_NO_MORE_HTLCS));
peer_unwatch_htlc_output(peer, idata->htlc,
INPUT_NO_MORE_HTLCS);
return unchanged_state(cstatus);
}
}
@ -871,19 +834,14 @@ enum command_status state(const tal_t *ctx,
if (input_is(input, BITCOIN_ANCHOR_THEIRSPEND)) {
tx = bitcoin_spend_theirs(ctx, peer, idata->btc);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch(ctx, tx,
BITCOIN_SPEND_THEIRS_DONE));
/* HTLC watches. */
htlcs = htlc_outputs_their_commit(ctx, peer,
idata->btc,
peer_watch_tx(peer, tx, BITCOIN_SPEND_THEIRS_DONE);
/* HTLC watches: if any, set HTLCs bit. */
if (peer_watch_their_htlc_outputs(peer, idata->btc,
BITCOIN_HTLC_TOUS_TIMEOUT,
BITCOIN_HTLC_TOTHEM_SPENT,
BITCOIN_HTLC_TOTHEM_TIMEOUT);
if (htlcs) {
add_effect(effect, watch_htlcs, htlcs);
BITCOIN_HTLC_TOTHEM_TIMEOUT))
bits |= STATE_CLOSE_HTLCS_BIT;
}
bits |= STATE_CLOSE_SPENDTHEM_BIT;
return next_state_bits(peer, cstatus, bits);
/* This can happen multiple times: need to steal ALL */
@ -893,9 +851,7 @@ enum command_status state(const tal_t *ctx,
return next_state(peer, cstatus,
STATE_ERR_INFORMATION_LEAK);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch(ctx, tx,
BITCOIN_STEAL_DONE));
peer_watch_tx(peer, tx, BITCOIN_STEAL_DONE);
bits |= STATE_CLOSE_STEAL_BIT;
return next_state_bits(peer, cstatus, bits);
} else if (input_is(input, BITCOIN_ANCHOR_UNSPENT))
@ -981,20 +937,16 @@ start_unilateral_close:
set_peer_cond(peer, PEER_CLOSED);
tx = bitcoin_commit(ctx, peer);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch_delayed(ctx, tx,
BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED));
peer_watch_delayed(peer, tx, BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED);
/* HTLC watches. */
htlcs = htlc_outputs_our_commit(ctx, peer, tx,
if (peer_watch_our_htlc_outputs(peer, tx,
BITCOIN_HTLC_TOUS_TIMEOUT,
BITCOIN_HTLC_TOTHEM_SPENT,
BITCOIN_HTLC_TOTHEM_TIMEOUT);
if (htlcs) {
add_effect(effect, watch_htlcs, htlcs);
BITCOIN_HTLC_TOTHEM_TIMEOUT))
return next_state(peer, cstatus,
STATE_CLOSE_WAIT_OURCOMMIT_WITH_HTLCS);
}
return next_state(peer, cstatus, STATE_CLOSE_WAIT_OURCOMMIT);
err_start_unilateral_close_already_closing:
@ -1011,9 +963,7 @@ start_unilateral_close_already_closing:
set_peer_cond(peer, PEER_CLOSED);
tx = bitcoin_commit(ctx, peer);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch_delayed(ctx, tx,
BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED));
peer_watch_delayed(peer, tx, BITCOIN_ANCHOR_OURCOMMIT_DELAYPASSED);
/* We agreed to close: shouldn't have any HTLCs */
if (committed_to_htlcs(peer))
@ -1033,21 +983,17 @@ them_unilateral:
set_peer_cond(peer, PEER_CLOSED);
tx = bitcoin_spend_theirs(ctx, peer, idata->btc);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch(ctx, tx,
BITCOIN_SPEND_THEIRS_DONE));
peer_watch_tx(peer, 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(ctx, peer, idata->btc,
if (peer_watch_their_htlc_outputs(peer, idata->btc,
BITCOIN_HTLC_TOUS_TIMEOUT,
BITCOIN_HTLC_TOTHEM_SPENT,
BITCOIN_HTLC_TOTHEM_TIMEOUT);
if (htlcs) {
add_effect(effect, watch_htlcs, htlcs);
BITCOIN_HTLC_TOTHEM_TIMEOUT))
return next_state(peer, cstatus,
STATE_CLOSE_WAIT_SPENDTHEM_WITH_HTLCS);
}
return next_state(peer, cstatus, STATE_CLOSE_WAIT_SPENDTHEM);
accept_htlc_update:
@ -1100,10 +1046,7 @@ start_closing:
err = pkt_err(ctx, "Close forced due to HTLCs");
goto err_start_unilateral_close;
}
add_effect(effect, close_timeout, INPUT_CLOSE_COMPLETE_TIMEOUT);
add_effect(effect, watch,
bitcoin_watch_close(ctx, peer, BITCOIN_CLOSE_DONE));
peer_watch_close(peer, BITCOIN_CLOSE_DONE, INPUT_CLOSE_COMPLETE_TIMEOUT);
/* No more commands, we're already closing. */
set_peer_cond(peer, PEER_CLOSING);
@ -1116,9 +1059,9 @@ accept_closing:
err = accept_pkt_close(ctx, peer, idata->pkt, effect);
if (err)
goto err_start_unilateral_close;
/* As soon as we send packet, they could close. */
add_effect(effect, watch,
bitcoin_watch_close(ctx, peer, BITCOIN_CLOSE_DONE));
peer_watch_close(peer, BITCOIN_CLOSE_DONE, INPUT_NONE);
/* Send close TX. */
queue_tx_broadcast(broadcast, bitcoin_close(ctx, peer));
queue_pkt(out, pkt_close_complete(ctx, peer));
/* No more commands, we're already closing. */
set_peer_cond(peer, PEER_CLOSING);
@ -1151,18 +1094,14 @@ fail_during_close:
/* A reorganization could make this happen. */
tx = bitcoin_spend_theirs(ctx, peer, idata->btc);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch(ctx, tx,
BITCOIN_SPEND_THEIRS_DONE));
htlcs = htlc_outputs_their_commit(ctx, peer, idata->btc,
peer_watch_tx(peer, tx, BITCOIN_SPEND_THEIRS_DONE);
if (peer_watch_their_htlc_outputs(peer, idata->btc,
BITCOIN_HTLC_TOUS_TIMEOUT,
BITCOIN_HTLC_TOTHEM_SPENT,
BITCOIN_HTLC_TOTHEM_TIMEOUT);
/* Expect either close or spendthem to complete */
if (htlcs) {
BITCOIN_HTLC_TOTHEM_TIMEOUT)) {
/* Expect either close or spendthem to complete */
/* FIXME: Make sure caller uses INPUT_RVAL
* if they were in the middle of FULFILL! */
add_effect(effect, watch_htlcs, htlcs);
return next_state(peer, cstatus,
STATE_CLOSE_WAIT_SPENDTHEM_CLOSE_WITH_HTLCS);
}
@ -1174,8 +1113,7 @@ fail_during_close:
return next_state(peer, cstatus,
STATE_ERR_INFORMATION_LEAK);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch(ctx, tx, BITCOIN_STEAL_DONE));
peer_watch_tx(peer, tx, BITCOIN_STEAL_DONE);
/* Expect either close or steal to complete */
return next_state(peer, cstatus,
STATE_CLOSE_WAIT_STEAL_CLOSE);
@ -1199,7 +1137,6 @@ old_commit_spotted:
return next_state(peer, cstatus,
STATE_ERR_INFORMATION_LEAK);
queue_tx_broadcast(broadcast, tx);
add_effect(effect, watch,
bitcoin_watch(ctx, tx, BITCOIN_STEAL_DONE));
peer_watch_tx(peer, tx, BITCOIN_STEAL_DONE);
return next_state(peer, cstatus, STATE_CLOSE_WAIT_STEAL);
}

190
state.h

@ -6,51 +6,13 @@
#include <state_types.h>
#include <stdbool.h>
enum state_effect_type {
STATE_EFFECT_watch,
STATE_EFFECT_unwatch,
/* FIXME: Use a watch for this?. */
STATE_EFFECT_close_timeout,
/* 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.
*
* Calling the state machine with an input simply returns the new state,
* and populates the "effect" struct with what it wants done.
* Calling the state machine updates updates peer->state, and may call
* various peer_ callbacks. It also returns the status of the current
* command.
*/
struct state_effect {
struct state_effect *next;
enum state_effect_type etype;
union {
/* Event to watch for. */
struct watch *watch;
/* Events to no longer watch for. */
struct watch *unwatch;
/* Set a timeout for close tx. */
enum state_input close_timeout;
/* HTLC outputs to watch. */
const struct htlc_watch *watch_htlcs;
/* 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;
/* FIXME: More to come (for accept_*) */
} u;
};
static inline bool state_is_error(enum state s)
{
@ -59,6 +21,7 @@ static inline bool state_is_error(enum state s)
struct peer;
struct bitcoin_tx;
struct state_effect;
static inline bool input_is_pkt(enum state_input input)
{
@ -226,8 +189,7 @@ Pkt *accept_pkt_close_ack(const tal_t *ctx,
bool committed_to_htlcs(const struct peer *peer);
/**
* bitcoin_watch_anchor: create a watch for the anchor.
* @ctx: context to tal the watch struct off.
* peer_watch_anchor: create a watch for the anchor transaction.
* @peer: the state data for this peer.
* @depthok: the input to give when anchor reaches expected depth.
* @timeout: the input to give if anchor doesn't reach depth in time.
@ -238,136 +200,138 @@ bool committed_to_htlcs(const struct peer *peer);
* @depthok can be INPUT_NONE if it's our anchor (we don't time
* ourselves out).
*/
struct watch *bitcoin_watch_anchor(const tal_t *ctx,
const struct peer *peer,
enum state_input depthok,
enum state_input timeout,
enum state_input unspent,
enum state_input theyspent,
enum state_input otherspent);
void peer_watch_anchor(struct peer *peer,
enum state_input depthok,
enum state_input timeout,
enum state_input unspent,
enum state_input theyspent,
enum state_input otherspent);
/**
* bitcoin_unwatch_anchor_depth: remove depth watch for the anchor.
* @ctx: context to tal the watch struct off.
* peer_unwatch_anchor_depth: remove depth watch for the anchor.
* @peer: the state data for this peer.
* @depthok: the input to give when anchor reaches expected depth.
* @timeout: the input to give if anchor doesn't reach depth in time.
*
* @depthok and @timeout must match bitcoin_watch_anchor() call.
*/
struct watch *bitcoin_unwatch_anchor_depth(const tal_t *ctx,
const struct peer *peer,
enum state_input depthok,
enum state_input timeout);
void peer_unwatch_anchor_depth(struct peer *peer,
enum state_input depthok,
enum state_input timeout);
/**
* bitcoin_watch_delayed: watch this (commit) tx, tell me when I can spend it
* @ctx: the context to tal the watch off
* peer_watch_delayed: watch this (commit) tx, tell me when I can spend it
* @peer: the state data for this peer.
* @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 tal_t *ctx,
const struct bitcoin_tx *tx,
enum state_input canspend);
void peer_watch_delayed(struct peer *peer,
const struct bitcoin_tx *tx,
enum state_input canspend);
/**
* bitcoin_watch: watch this tx until it's "irreversible"
* @ctx: the context to tal the watch off
* peer_watch_tx: watch this tx until it's "irreversible"
* @peer: the state data for this peer.
* @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).
* Once this fires we consider the channel completely closed and stop
* watching (eg 100 txs down).
*
* This is used for watching a transaction we sent (such as a steal,
* or spend of their close, etc).
*/
struct watch *bitcoin_watch(const tal_t *ctx,
const struct bitcoin_tx *tx,
enum state_input done);
void peer_watch_tx(struct peer *peer,
const struct bitcoin_tx *tx,
enum state_input done);
/**
* bitcoin_watch_close: watch close tx until it's "irreversible"
* @ctx: context to tal the watch struct off.
* peer_watch_close: watch for close tx until it's "irreversible" (or timedout)
* @peer: the state data for this peer.
* @done: the input to give when tx is completely buried.
* @timedout: the input to give if we time out.
*
* Once this fires we consider the channel completely closed and stop
* watching (eg 100 txs down).
*
* This tx *is* malleable, since the other side can transmit theirs.
* This is used for watching a mutual close, or for a transaction we sent
* (such as a steal, or spend of their close, etc).
*/
struct watch *bitcoin_watch_close(const tal_t *ctx,
const struct peer *peer,
enum state_input done);
void peer_watch_close(struct peer *peer,
enum state_input done, enum state_input timedout);
/**
* htlc_outputs_our_commit: HTLC outputs from our commit tx to watch.
* @ctx: context to tal the watch struct off.
* peer_watch_our_htlc_outputs: HTLC outputs from our commit tx to watch.
* @peer: the state data for this peer.
* @tx: the commitment tx
* @tous_timeout: input to give when a HTLC output to us times out.
* @tothem_spent: input to give when a HTLC output to them is spent.
* @tothem_timeout: input to give when a HTLC output to them times out.
*
* Returns true if there were any htlc outputs to watch.
*/
struct htlc_watch *htlc_outputs_our_commit(const tal_t *ctx,
const struct peer *peer,
const struct bitcoin_tx *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout);
bool peer_watch_our_htlc_outputs(struct peer *peer,
const struct bitcoin_tx *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout);
/**
* htlc_outputs_their_commit: HTLC outputs from their commit tx to watch.
* @ctx: context to tal the watch struct off.
* peer_watch_their_htlc_outputs: HTLC outputs from their commit tx to watch.
* @peer: the state data for this peer.
* @tx: the commitment tx
* @tous_timeout: input to give when a HTLC output to us times out.
* @tothem_spent: input to give when a HTLC output to them is spent.
* @tothem_timeout: input to give when a HTLC output to them times out.
*
* Returns true if there were any htlc outputs to watch.
*/
struct htlc_watch *htlc_outputs_their_commit(const tal_t *ctx,
const struct peer *peer,
const struct bitcoin_event *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout);
bool peer_watch_their_htlc_outputs(struct peer *peer,
const struct bitcoin_event *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout);
/**
* htlc_unwatch: stop watching an HTLC
* @ctx: context to tal the watch struct off.
* peer_unwatch_htlc_output: stop watching an HTLC
* @peer: the state data for this peer.
* @htlc: the htlc to stop watching
* @all_done: input to give if we're not watching any anymore.
* @all_done: input to give if we're not watching any outputs anymore.
*/
struct htlc_unwatch *htlc_unwatch(const tal_t *ctx,
const struct htlc *htlc,
enum state_input all_done);
void peer_unwatch_htlc_output(struct peer *peer,
const struct htlc *htlc,
enum state_input all_done);
/**
* htlc_unwatch_all: stop watching all HTLCs
* @ctx: context to tal the watch struct off.
* peer_unwatch_all_htlc_outputs: stop watching all HTLCs
* @peer: the state data for this peer.
*/
struct htlc_unwatch *htlc_unwatch_all(const tal_t *ctx,
const struct peer *peer);
void peer_unwatch_all_htlc_outputs(struct peer *peer);
/**
* htlc_spend_watch: watch our spend of an HTLC
* @ctx: context to tal the watch struct off.
* peer_watch_htlc_spend: watch our spend of an HTLC output
* @peer: the state data for this peer.
* @tx: the commitment tx
* @cmd: the command data.
* @htlc: the htlc the tx is spending an output of
* @done: input to give when it's completely buried.
*/
struct htlc_spend_watch *htlc_spend_watch(const tal_t *ctx,
const struct bitcoin_tx *tx,
const struct command *cmd,
enum state_input done);
void peer_watch_htlc_spend(struct peer *peer,
const struct bitcoin_tx *tx,
const struct htlc *htlc,
enum state_input done);
/**
* htlc_spend_unwatch: stop watching an HTLC spend
* @ctx: context to tal the watch struct off.
* @htlc: the htlc to stop watching
* peer_unwatch_htlc_spend: stop watching our HTLC spend
* @peer: the state data for this peer.
* @htlc: the htlc to stop watching the spend for.
* @all_done: input to give if we're not watching anything anymore.
*/
struct htlc_spend_watch *htlc_spend_unwatch(const tal_t *ctx,
const struct htlc *htlc,
enum state_input all_done);
void peer_unwatch_htlc_spend(struct peer *peer,
const struct htlc *htlc,
enum state_input all_done);
/* Create a bitcoin anchor tx. */
struct bitcoin_tx *bitcoin_anchor(const tal_t *ctx,
const struct peer *peer);

722
test/test_state_coverage.c

@ -871,134 +871,6 @@ struct bitcoin_tx *bitcoin_anchor(const tal_t *ctx,
return bitcoin_tx("anchor");
}
static bool have_event(uint64_t events, enum state_input input)
{
return events & (1ULL << input);
}
static bool add_event_(uint64_t *events, enum state_input input)
{
/* This is how they say "no event please" */
if (input == INPUT_NONE)
return true;
assert(input < 64);
if (have_event(*events, input))
return false;
*events |= (1ULL << input);
return true;
}
static bool remove_event_(uint64_t *events, enum state_input input)
{
/* This is how they say "no event please" */
if (input == INPUT_NONE)
return true;
assert(input < 64);
if (!have_event(*events, input))
return false;
*events &= ~(1ULL << input);
return true;
}
static void remove_event(uint64_t *events, enum state_input input)
{
#ifdef NDEBUG
#error "Don't run tests with NDEBUG"
#endif
assert(remove_event_(events, input));
}
static void add_event(uint64_t *events, enum state_input input)
{
#ifdef NDEBUG
#error "Don't run tests with NDEBUG"
#endif
assert(add_event_(events, input));
}
struct watch {
uint64_t events;
};
struct watch *bitcoin_watch_anchor(const tal_t *ctx,
const struct peer *peer,
enum state_input depthok,
enum state_input timeout,
enum state_input unspent,
enum state_input theyspent,
enum state_input otherspent)
{
struct watch *watch = talz(ctx, struct watch);
add_event(&watch->events, depthok);
add_event(&watch->events, timeout);
add_event(&watch->events, unspent);
add_event(&watch->events, theyspent);
add_event(&watch->events, otherspent);
/* We assume these values in activate_event. */
assert(timeout == BITCOIN_ANCHOR_TIMEOUT
|| timeout == INPUT_NONE);
assert(depthok == BITCOIN_ANCHOR_DEPTHOK);
return watch;
}
struct watch *bitcoin_unwatch_anchor_depth(const tal_t *ctx,
const struct peer *peer,
enum state_input depthok,
enum state_input timeout)
{
struct watch *watch = talz(ctx, struct watch);
add_event(&watch->events, depthok);
add_event(&watch->events, timeout);
return watch;
}
/* Wait for our commit to be spendable. */
struct watch *bitcoin_watch_delayed(const tal_t *ctx,
const struct bitcoin_tx *tx,
enum state_input canspend)
{
struct watch *watch = talz(ctx, struct watch);
assert(bitcoin_tx_is(tx, "our commit"));
add_event(&watch->events, canspend);
return watch;
}
/* Wait for commit to be very deeply buried (so we no longer need to
* even watch) */
struct watch *bitcoin_watch(const tal_t *ctx,
const struct bitcoin_tx *tx,
enum state_input done)
{
struct watch *watch = talz(ctx, struct watch);
if (done == BITCOIN_STEAL_DONE)
assert(bitcoin_tx_is(tx, "steal"));
else if (done == BITCOIN_SPEND_THEIRS_DONE)
assert(bitcoin_tx_is(tx, "spend their commit"));
else if (done == BITCOIN_SPEND_OURS_DONE)
assert(bitcoin_tx_is(tx, "spend our commit"));
else
errx(1, "Unknown watch effect %s", input_name(done));
add_event(&watch->events, done);
return watch;
}
/* Other side should drop close tx; watch for it. */
struct watch *bitcoin_watch_close(const tal_t *ctx,
const struct peer *peer,
enum state_input done)
{
struct watch *watch = talz(ctx, struct watch);
add_event(&watch->events, done);
return watch;
}
struct bitcoin_tx *bitcoin_close(const tal_t *ctx,
const struct peer *peer)
{
@ -1054,143 +926,6 @@ bool committed_to_htlcs(const struct peer *peer)
return peer->num_htlcs_to_them != 0 || peer->num_htlcs_to_us != 0;
}
struct htlc_watch
{
enum state_input tous_timeout;
enum state_input tothem_spent;
enum state_input tothem_timeout;
unsigned int num_htlcs_to_us, num_htlcs_to_them;
struct htlc htlcs_to_us[MAX_HTLCS], htlcs_to_them[MAX_HTLCS];
};
struct htlc_unwatch
{
unsigned int id;
enum state_input all_done;
};
struct htlc_watch *htlc_outputs_our_commit(const tal_t *ctx,
const struct peer *peer,
const struct bitcoin_tx *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout)
{
struct htlc_watch *w = tal(ctx, struct htlc_watch);
/* We assume these. */
assert(tous_timeout == BITCOIN_HTLC_TOUS_TIMEOUT);
assert(tothem_spent == BITCOIN_HTLC_TOTHEM_SPENT);
assert(tothem_timeout == BITCOIN_HTLC_TOTHEM_TIMEOUT);
w->tous_timeout = tous_timeout;
w->tothem_spent = tothem_spent;
w->tothem_timeout = tothem_timeout;
w->num_htlcs_to_us = peer->num_htlcs_to_us;
w->num_htlcs_to_them = peer->num_htlcs_to_them;
BUILD_ASSERT(sizeof(peer->htlcs_to_us) == sizeof(w->htlcs_to_us));
BUILD_ASSERT(sizeof(peer->htlcs_to_them) == sizeof(w->htlcs_to_them));
memcpy(w->htlcs_to_us, peer->htlcs_to_us, sizeof(peer->htlcs_to_us));
memcpy(w->htlcs_to_them, peer->htlcs_to_them,
sizeof(peer->htlcs_to_them));
if (!w->num_htlcs_to_us && !w->num_htlcs_to_them)
return tal_free(w);
return w;
}
struct htlc_watch *htlc_outputs_their_commit(const tal_t *ctx,
const struct peer *peer,
const struct bitcoin_event *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout)
{
struct htlc_watch *w = tal(ctx, struct htlc_watch);
unsigned int i;
/* We assume these. */
assert(tous_timeout == BITCOIN_HTLC_TOUS_TIMEOUT);
assert(tothem_spent == BITCOIN_HTLC_TOTHEM_SPENT);
assert(tothem_timeout == BITCOIN_HTLC_TOTHEM_TIMEOUT);
w->tous_timeout = tous_timeout;
w->tothem_spent = tothem_spent;
w->tothem_timeout = tothem_timeout;
/* It's what our peer thinks is current... */
w->num_htlcs_to_us = peer->other->num_htlcs_to_them;
w->num_htlcs_to_them = peer->other->num_htlcs_to_us;
BUILD_ASSERT(sizeof(peer->other->htlcs_to_them) == sizeof(w->htlcs_to_us));
BUILD_ASSERT(sizeof(peer->other->htlcs_to_us) == sizeof(w->htlcs_to_them));
memcpy(w->htlcs_to_us, peer->other->htlcs_to_them, sizeof(w->htlcs_to_us));
memcpy(w->htlcs_to_them, peer->other->htlcs_to_us,
sizeof(w->htlcs_to_them));
if (!w->num_htlcs_to_us && !w->num_htlcs_to_them)
return tal_free(w);
/* Reverse perspective, mark rvalue unknown */
for (i = 0; i < w->num_htlcs_to_us; i++) {
assert(w->htlcs_to_us[i].to_them);
w->htlcs_to_us[i].to_them = false;
}
for (i = 0; i < w->num_htlcs_to_them; i++) {
assert(!w->htlcs_to_them[i].to_them);
w->htlcs_to_them[i].to_them = true;
}
return w;
}
struct htlc_unwatch *htlc_unwatch(const tal_t *ctx,
const struct htlc *htlc,
enum state_input all_done)
{
struct htlc_unwatch *w = tal(ctx, struct htlc_unwatch);
w->id = htlc->id;
assert(w->id != -1U);
w->all_done = all_done;
return w;
}
struct htlc_unwatch *htlc_unwatch_all(const tal_t *ctx,
const struct peer *peer)
{
struct htlc_unwatch *w = tal(ctx, struct htlc_unwatch);
w->id = -1U;
return w;
}
struct htlc_spend_watch *htlc_spend_watch(const tal_t *ctx,
const struct bitcoin_tx *tx,
const struct command *cmd,
enum state_input done)
{
struct htlc_spend_watch *w = tal(ctx, struct htlc_spend_watch);
w->id = htlc_id_from_tx(tx);
w->done = done;
return w;
}
struct htlc_spend_watch *htlc_spend_unwatch(const tal_t *ctx,
const struct htlc *htlc,
enum state_input all_done)
{
struct htlc_spend_watch *w = tal(ctx, struct htlc_spend_watch);
w->id = htlc->id;
w->done = all_done;
return w;
}
struct htlc_rval {
unsigned int id;
};
#include "state.c"
#include <ccan/tal/tal.h>
#include <stdio.h>
@ -1401,6 +1136,282 @@ static bool outstanding_htlc_watches(const struct peer *peer)
|| peer->num_htlc_spends_to_them;
}
static bool have_event(uint64_t events, enum state_input input)
{
return events & (1ULL << input);
}
static bool add_event_(struct peer *peer, enum state_input input)
{
/* This is how they say "no event please" */
if (input == INPUT_NONE)
return true;
assert(input < 64);
if (have_event(peer->core.event_notifies, input))
return false;
peer->core.event_notifies |= (1ULL << input);
return true;
}
static bool remove_event_(uint64_t *events, enum state_input input)
{
/* This is how they say "no event please" */
if (input == INPUT_NONE)
return true;
assert(input < 64);
if (!have_event(*events, input))
return false;
*events &= ~(1ULL << input);
return true;
}
static void remove_event(struct peer *peer, enum state_input input)
{
if (!remove_event_(&peer->core.event_notifies, input))
report_trail(peer->trail, "Removing event we don't have?");
}
static void add_event(struct peer *peer, enum state_input input)
{
if (!add_event_(peer, input))
report_trail(peer->trail, "Adding event we already have?");
}
void peer_watch_anchor(struct peer *peer,
enum state_input depthok,
enum state_input timeout,
enum state_input unspent,
enum state_input theyspent,
enum state_input otherspent)
{
/* We assume these values in activate_event. */
assert(timeout == BITCOIN_ANCHOR_TIMEOUT
|| timeout == INPUT_NONE);
assert(depthok == BITCOIN_ANCHOR_DEPTHOK);
add_event(peer, depthok);
add_event(peer, timeout);
add_event(peer, unspent);
add_event(peer, theyspent);
add_event(peer, otherspent);
}
void peer_unwatch_anchor_depth(struct peer *peer,
enum state_input depthok,
enum state_input timeout)
{
/* We assume these values in activate_event. */
assert(timeout == BITCOIN_ANCHOR_TIMEOUT
|| timeout == INPUT_NONE);
assert(depthok == BITCOIN_ANCHOR_DEPTHOK);
remove_event(peer, depthok);
remove_event(peer, timeout);
}
/* Wait for our commit to be spendable. */
void peer_watch_delayed(struct peer *peer,
const struct bitcoin_tx *tx, enum state_input canspend)
{
assert(bitcoin_tx_is(tx, "our commit"));
add_event(peer, canspend);
}
/* Wait for commit to be very deeply buried (so we no longer need to
* even watch) */
void peer_watch_tx(struct peer *peer,
const struct bitcoin_tx *tx,
enum state_input done)
{
/* We can have multiple steals or spendtheirs in flight, so
* allow repeats for
* BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */
if (done == BITCOIN_STEAL_DONE) {
assert(bitcoin_tx_is(tx, "steal"));
add_event_(peer, done);
} else if (done == BITCOIN_SPEND_THEIRS_DONE) {
assert(bitcoin_tx_is(tx, "spend their commit"));
add_event_(peer, done);
} else if (done == BITCOIN_SPEND_OURS_DONE) {
assert(bitcoin_tx_is(tx, "spend our commit"));
add_event(peer, done);
} else
report_trail(peer->trail, "Unknown watch effect");
}
/* Other side should drop close tx; watch for it. */
void peer_watch_close(struct peer *peer,
enum state_input done, enum state_input timedout)
{
add_event(peer, done);
/* We assume this. */
assert(timedout == INPUT_CLOSE_COMPLETE_TIMEOUT || timedout == INPUT_NONE);
add_event(peer, timedout);
}
bool peer_watch_our_htlc_outputs(struct peer *peer,
const struct bitcoin_tx *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout)
{
/* FIXME: We assume these. */
assert(tous_timeout == BITCOIN_HTLC_TOUS_TIMEOUT);
assert(tothem_spent == BITCOIN_HTLC_TOTHEM_SPENT);
assert(tothem_timeout == BITCOIN_HTLC_TOTHEM_TIMEOUT);
if (!peer->num_htlcs_to_us && !peer->num_htlcs_to_them)
return false;
assert(peer->num_live_htlcs_to_us + peer->num_htlcs_to_us
<= ARRAY_SIZE(peer->live_htlcs_to_us));
assert(peer->num_live_htlcs_to_them + peer->num_htlcs_to_them
<= ARRAY_SIZE(peer->live_htlcs_to_them));
memcpy(peer->live_htlcs_to_us + peer->num_live_htlcs_to_us,
peer->htlcs_to_us,
peer->num_htlcs_to_us * sizeof(peer->htlcs_to_us[0]));
memcpy(peer->live_htlcs_to_them + peer->num_live_htlcs_to_them,
peer->htlcs_to_them,
peer->num_htlcs_to_them * sizeof(peer->htlcs_to_them[0]));
peer->num_live_htlcs_to_us += peer->num_htlcs_to_us;
peer->num_live_htlcs_to_them += peer->num_htlcs_to_them;
/* Can happen if we were finished, then new commit tx */
remove_event_(&peer->core.event_notifies, INPUT_NO_MORE_HTLCS);
return true;
}
bool peer_watch_their_htlc_outputs(struct peer *peer,
const struct bitcoin_event *tx,
enum state_input tous_timeout,
enum state_input tothem_spent,
enum state_input tothem_timeout)
{
size_t i;
struct htlc *htlcs;
/* We assume these. */
assert(tous_timeout == BITCOIN_HTLC_TOUS_TIMEOUT);
assert(tothem_spent == BITCOIN_HTLC_TOTHEM_SPENT);
assert(tothem_timeout == BITCOIN_HTLC_TOTHEM_TIMEOUT);
/* It's what our peer thinks is current... */
if (!peer->other->num_htlcs_to_them && !peer->other->num_htlcs_to_us)
return false;
assert(peer->num_live_htlcs_to_us + peer->other->num_htlcs_to_them
<= ARRAY_SIZE(peer->live_htlcs_to_us));
assert(peer->num_live_htlcs_to_them + peer->other->num_htlcs_to_us
<= ARRAY_SIZE(peer->live_htlcs_to_them));
/* Copy from other peer, but reverse perspective */
htlcs = peer->live_htlcs_to_us + peer->num_live_htlcs_to_us;
memcpy(htlcs, peer->other->htlcs_to_them,
peer->other->num_htlcs_to_them * sizeof(peer->htlcs_to_us[0]));
for (i = 0; i < peer->other->num_htlcs_to_them; i++) {
assert(htlcs[i].to_them);
htlcs[i].to_them = false;
}
htlcs = peer->live_htlcs_to_them + peer->num_live_htlcs_to_them;
memcpy(htlcs,
peer->other->htlcs_to_us,
peer->other->num_htlcs_to_us * sizeof(peer->htlcs_to_them[0]));
for (i = 0; i < peer->other->num_htlcs_to_us; i++) {
assert(!htlcs[i].to_them);
htlcs[i].to_them = true;
}
peer->num_live_htlcs_to_us += peer->other->num_htlcs_to_them;
peer->num_live_htlcs_to_them += peer->other->num_htlcs_to_us;
/* Can happen if we were finished, then new commit tx */
remove_event_(&peer->core.event_notifies, INPUT_NO_MORE_HTLCS);
return true;
}
void peer_unwatch_htlc_output(struct peer *peer,
const struct htlc *htlc,
enum state_input all_done)
{
const struct htlc *h;
/* We assume this. */
assert(all_done == INPUT_NO_MORE_HTLCS);
h = find_live_htlc(peer, htlc->id);
/* That can fail, when we see them spend (and thus stop
* watching) after we've timed out, then our return tx wins
* and gets buried. */
if (h) {
remove_htlc(peer->live_htlcs_to_us,
&peer->num_live_htlcs_to_us,
peer->live_htlcs_to_them,
&peer->num_live_htlcs_to_them,
ARRAY_SIZE(peer->live_htlcs_to_us),
h);
/* If that was last, fire INPUT_NO_MORE_HTLCS */
if (!outstanding_htlc_watches(peer))
add_event(peer, all_done);
}
}
void peer_unwatch_all_htlc_outputs(struct peer *peer)
{
/* This can happen if we get in front of INPUT_NO_MORE_HTLCS */
if (!outstanding_htlc_watches(peer)
&& !have_event(peer->core.event_notifies, INPUT_NO_MORE_HTLCS))
report_trail(peer->trail, "unwatching all with no htlcs?");
peer->num_htlc_spends_to_us = 0;
peer->num_htlc_spends_to_them = 0;
peer->num_live_htlcs_to_us = 0;
peer->num_live_htlcs_to_them = 0;
}
void peer_watch_htlc_spend(struct peer *peer,
const struct bitcoin_tx *tx,
const struct htlc *htlc,
enum state_input done)
{
struct htlc *h = find_live_htlc(peer, htlc_id_from_tx(tx));
add_htlc(peer->htlc_spends_to_us, &peer->num_htlc_spends_to_us,
peer->htlc_spends_to_them,
&peer->num_htlc_spends_to_them,
ARRAY_SIZE(peer->htlc_spends_to_us),
h);
/* We assume this */
if (h->to_them)
assert(done == BITCOIN_HTLC_RETURN_SPEND_DONE);
else
assert(done == BITCOIN_HTLC_FULFILL_SPEND_DONE);
}
void peer_unwatch_htlc_spend(struct peer *peer,
const struct htlc *htlc,
enum state_input all_done)
{
struct htlc *h = find_htlc_spend(peer, htlc->id);
assert(all_done == INPUT_NO_MORE_HTLCS);
remove_htlc(peer->htlc_spends_to_us,
&peer->num_htlc_spends_to_us,
peer->htlc_spends_to_them,
&peer->num_htlc_spends_to_them,
ARRAY_SIZE(peer->htlc_spends_to_us),
h);
if (!outstanding_htlc_watches(peer))
add_event(peer, all_done);
}
void peer_unexpected_pkt(struct peer *peer, const Pkt *pkt)
{
const char *str = (const char *)pkt;
@ -1471,159 +1482,6 @@ void peer_tx_revealed_r_value(struct peer *peer,
add_rval(peer, htlc->id);
}
/* We apply them backwards, which helps our assertions. It's not actually
* required. */
static const char *apply_effects(struct peer *peer,
const struct state_effect *effect,
uint64_t *effects)
{
const struct htlc *h;
if (!effect)
return NULL;
if (effect->next) {
const char *problem = apply_effects(peer, effect->next,
effects);
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_watch:
/* We can have multiple steals or spendtheirs
in flight, so make exceptions for
BITCOIN_STEAL_DONE/BITCOIN_SPEND_THEIRS_DONE */
if (peer->core.event_notifies & (1ULL << BITCOIN_STEAL_DONE)
& effect->u.watch->events)
remove_event(&effect->u.watch->events, BITCOIN_STEAL_DONE);
if (peer->core.event_notifies
& (1ULL << BITCOIN_SPEND_THEIRS_DONE)
& effect->u.watch->events)
remove_event(&effect->u.watch->events,
BITCOIN_SPEND_THEIRS_DONE);
if (peer->core.event_notifies & effect->u.watch->events)
return "event set twice";
peer->core.event_notifies |= effect->u.watch->events;
break;
case STATE_EFFECT_unwatch:
if ((peer->core.event_notifies & effect->u.unwatch->events)
!= effect->u.unwatch->events)
return "unset event unwatched";
peer->core.event_notifies &= ~effect->u.unwatch->events;
break;
case STATE_EFFECT_close_timeout:
add_event(&peer->core.event_notifies,
effect->u.close_timeout);
/* We assume this. */
assert(effect->u.close_timeout
== INPUT_CLOSE_COMPLETE_TIMEOUT);
break;
case STATE_EFFECT_watch_htlcs:
assert(peer->num_live_htlcs_to_us
+ effect->u.watch_htlcs->num_htlcs_to_us
<= ARRAY_SIZE(peer->live_htlcs_to_us));
assert(peer->num_live_htlcs_to_them
+ effect->u.watch_htlcs->num_htlcs_to_them
<= ARRAY_SIZE(peer->live_htlcs_to_them));
memcpy(peer->live_htlcs_to_us + peer->num_live_htlcs_to_us,
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(peer->live_htlcs_to_them + peer->num_live_htlcs_to_them,
effect->u.watch_htlcs->htlcs_to_them,
effect->u.watch_htlcs->num_htlcs_to_them
* sizeof(effect->u.watch_htlcs->htlcs_to_them[0]));
peer->num_live_htlcs_to_us
+= effect->u.watch_htlcs->num_htlcs_to_us;
peer->num_live_htlcs_to_them
+= effect->u.watch_htlcs->num_htlcs_to_them;
/* Can happen if we were finished, then new commit tx */
remove_event_(&peer->core.event_notifies, INPUT_NO_MORE_HTLCS);
break;
case STATE_EFFECT_unwatch_htlc:
/* Unwatch all? */
if (effect->u.unwatch_htlc->id == -1) {
/* This can happen if we get in front of
* INPUT_NO_MORE_HTLCS */
if (!outstanding_htlc_watches(peer)
&& !have_event(peer->core.event_notifies,
INPUT_NO_MORE_HTLCS))
return "unwatching all with no htlcs?";
peer->num_htlc_spends_to_us = 0;
peer->num_htlc_spends_to_them = 0;
peer->num_live_htlcs_to_us = 0;
peer->num_live_htlcs_to_them = 0;
} else {
const struct htlc *h;
h = find_live_htlc(peer, effect->u.unwatch_htlc->id);
/* That can fail, when we see them spend (and
* thus stop watching) after we've timed out,
* then our return tx wins and gets buried. */
if (h) {
remove_htlc(peer->live_htlcs_to_us,
&peer->num_live_htlcs_to_us,
peer->live_htlcs_to_them,
&peer->num_live_htlcs_to_them,
ARRAY_SIZE(peer->live_htlcs_to_us),
h);
/* If that was last, fire INPUT_NO_MORE_HTLCS */
if (!outstanding_htlc_watches(peer)) {
assert(effect->u.unwatch_htlc->all_done
== INPUT_NO_MORE_HTLCS);
add_event(&peer->core.event_notifies,
effect->u.unwatch_htlc->all_done);
}
}
}
break;
case STATE_EFFECT_watch_htlc_spend:
h = find_live_htlc(peer, effect->u.watch_htlc_spend->id);
add_htlc(peer->htlc_spends_to_us, &peer->num_htlc_spends_to_us,
peer->htlc_spends_to_them,
&peer->num_htlc_spends_to_them,
ARRAY_SIZE(peer->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(peer, effect->u.unwatch_htlc_spend->id);
remove_htlc(peer->htlc_spends_to_us,
&peer->num_htlc_spends_to_us,
peer->htlc_spends_to_them,
&peer->num_htlc_spends_to_them,
ARRAY_SIZE(peer->htlc_spends_to_us),
h);
if (!outstanding_htlc_watches(peer)) {
assert(effect->u.unwatch_htlc_spend->done
== INPUT_NO_MORE_HTLCS);
add_event(&peer->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 *check_changes(const struct peer *old, struct peer *new,
enum state_input input)
{
@ -1661,16 +1519,12 @@ static const char *check_changes(const struct peer *old, struct peer *new,
return NULL;
}
static const char *apply_all_effects(const struct peer *old,
static const char *check_all_effects(const struct peer *old,
enum command_status cstatus,
enum state_input input,
struct peer *peer,
const struct state_effect *effect,
Pkt *output)
{
const char *problem;
uint64_t effects = 0;
if (cstatus != CMD_NONE) {
assert(peer->core.current_command != INPUT_NONE);
/* We should only requeue HTLCs if we're lowprio */
@ -1717,11 +1571,17 @@ static const char *apply_all_effects(const struct peer *old,
peer->pkt_data[peer->core.num_outputs++]
= htlc_id_from_pkt(output);
}
problem = apply_effects(peer, effect, &effects);
if (!problem)
problem = check_changes(old, peer, input);
return problem;
if (peer->state >= STATE_CLOSE_WAIT_STEAL
&& peer->state <= STATE_CLOSE_WAIT_STEAL_SPENDTHEM_CLOSE_SPENDOURS_WITH_HTLCS) {
if (STATE_TO_BITS(peer->state) & STATE_CLOSE_HTLCS_BIT) {
if (!outstanding_htlc_watches(peer)
&& !have_event(peer->core.event_notifies, INPUT_NO_MORE_HTLCS))
return "CLOSE_HTLCS with no outstanding watches?";
}
}
return check_changes(old, peer, input);
}
static void eliminate_input(enum state_input **inputs, enum state_input in)
@ -1958,7 +1818,7 @@ static void try_input(const struct peer *peer,
add_dot(&hist->edges, oldstr, newstr, i, output);
}
problem = apply_all_effects(peer, cstatus, i, &copy, effect, output);
problem = check_all_effects(peer, cstatus, i, &copy, output);
update_trail(&t, &copy, output);
if (problem)
report_trail(&t, problem);
@ -2069,7 +1929,7 @@ static void activate_event(struct peer *peer, enum state_input i)
break;
case BITCOIN_ANCHOR_TIMEOUT:
/* Can't sent DEPTHOK */
remove_event(&peer->core.event_notifies, BITCOIN_ANCHOR_DEPTHOK);
remove_event(peer, BITCOIN_ANCHOR_DEPTHOK);
break;
/* And of the "done" cases means we won't give the others. */
case BITCOIN_SPEND_THEIRS_DONE:
@ -2160,7 +2020,7 @@ static void run_peer(const struct peer *peer,
/* Don't re-fire most events */
if (!can_refire(i))
remove_event(&copy.core.event_notifies, i);
remove_event(&copy, i);
activate_event(&copy, i);
try_input(&copy, i, idata, normalpath, errorpath,
prev_trail, hist);

Loading…
Cancel
Save