mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 07:34:24 +01:00
state: simplify effect.
Make it a linked list of effects, rather than one big union. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
215
state.h
215
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;
|
|
||||||
|
|
||||||
/* Packet to send. */
|
enum state_effect_type etype;
|
||||||
Pkt *send;
|
union {
|
||||||
|
/* New state to enter. */
|
||||||
|
enum state new_state;
|
||||||
|
|
||||||
/* Event to watch for. */
|
/* Transaction to broadcast. */
|
||||||
struct watch *watch;
|
struct bitcoin_tx *broadcast_tx;
|
||||||
|
|
||||||
/* Events to no longer watch for. */
|
/* Packet to send. */
|
||||||
struct watch *unwatch;
|
Pkt *send_pkt;
|
||||||
|
|
||||||
/* Defer an input. */
|
/* Event to watch for. */
|
||||||
enum state_input defer;
|
struct watch *watch;
|
||||||
|
|
||||||
/* Complete a command. */
|
/* Events to no longer watch for. */
|
||||||
enum state_input complete;
|
struct watch *unwatch;
|
||||||
enum cmd_complete_status status;
|
|
||||||
void *faildata;
|
|
||||||
|
|
||||||
/* Completing a CMD_CLOSE */
|
/* Defer an input. */
|
||||||
enum cmd_complete_status close_status;
|
enum state_input cmd_defer;
|
||||||
|
|
||||||
/* Stop taking packets? commands? */
|
/* Requeue/succeed/fail command. */
|
||||||
bool stop_packets, stop_commands;
|
enum state_input cmd_requeue;
|
||||||
|
enum state_input cmd_success;
|
||||||
|
void *cmd_fail;
|
||||||
|
|
||||||
/* Set a timeout for close tx. */
|
/* CMD_CLOSE is complete (true if successful mutual close). */
|
||||||
enum state_input close_timeout;
|
bool cmd_close_done;
|
||||||
|
|
||||||
/* Error received from other side. */
|
/* Stop taking packets? commands? */
|
||||||
Pkt *in_error;
|
bool stop_packets, stop_commands;
|
||||||
|
|
||||||
/* HTLC we're working on. */
|
/* Set a timeout for close tx. */
|
||||||
struct htlc_progress *htlc_in_progress;
|
enum state_input close_timeout;
|
||||||
|
|
||||||
/* Their signature for the new commit tx. */
|
/* Error received from other side. */
|
||||||
struct signature *update_theirsig;
|
Pkt *in_error;
|
||||||
|
|
||||||
|
/* HTLC we're working on. */
|
||||||
|
struct htlc_progress *htlc_in_progress;
|
||||||
|
|
||||||
|
/* Their signature for the new commit tx. */
|
||||||
|
struct signature *update_theirsig;
|
||||||
|
|
||||||
/* Stop working on HTLC. */
|
/* 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,
|
||||||
|
struct htlc_progress **htlcprog,
|
||||||
|
struct state_effect **effect);
|
||||||
|
|
||||||
|
Pkt *accept_pkt_update_accept(const tal_t *ctx,
|
||||||
const struct state_data *sdata, const Pkt *pkt,
|
const struct state_data *sdata, const Pkt *pkt,
|
||||||
struct htlc_progress **htlcprog);
|
struct signature **sig,
|
||||||
|
struct state_effect **effect);
|
||||||
|
|
||||||
Pkt *accept_pkt_update_accept(struct state_effect *effect,
|
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 signature **sig);
|
struct state_effect **effect);
|
||||||
|
|
||||||
Pkt *accept_pkt_update_complete(struct state_effect *effect,
|
Pkt *accept_pkt_update_signature(const tal_t *ctx,
|
||||||
const struct state_data *sdata, const Pkt *pkt);
|
|
||||||
|
|
||||||
Pkt *accept_pkt_update_signature(struct state_effect *effect,
|
|
||||||
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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *apply_effects(struct state_data *sdata,
|
/* Some assertions once they've already been applied. */
|
||||||
const struct state_effect *effect)
|
static char *check_effects(struct state_data *sdata,
|
||||||
|
const struct state_effect *effect)
|
||||||
{
|
{
|
||||||
if (effect->send) {
|
while (effect) {
|
||||||
const char *pkt = (const char *)effect->send;
|
if (effect->etype == STATE_EFFECT_in_error) {
|
||||||
|
/* We should stop talking to them after error recvd. */
|
||||||
|
if (sdata->core.pkt_inputs)
|
||||||
|
return "packets still open after error pkt";
|
||||||
|
} else if (effect->etype == STATE_EFFECT_stop_commands) {
|
||||||
|
if (sdata->core.current_command != INPUT_NONE)
|
||||||
|
return tal_fmt(NULL,
|
||||||
|
"stop_commands with pending command %s",
|
||||||
|
input_name(sdata->core.current_command));
|
||||||
|
if (sdata->core.closing_cmd)
|
||||||
|
return "stop_commands with pending CMD_CLOSE";
|
||||||
|
}
|
||||||
|
effect = effect->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We apply them backwards, which helps our assertions. It's not actually
|
||||||
|
* required. */
|
||||||
|
static const char *apply_effects(struct state_data *sdata,
|
||||||
|
const struct state_effect *effect,
|
||||||
|
uint64_t *effects,
|
||||||
|
Pkt **output)
|
||||||
|
{
|
||||||
|
const struct htlc *h;
|
||||||
|
|
||||||
|
if (!effect)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (effect->next) {
|
||||||
|
const char *problem = apply_effects(sdata, effect->next,
|
||||||
|
effects, output);
|
||||||
|
if (problem)
|
||||||
|
return problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*effects & (1ULL << effect->etype))
|
||||||
|
return tal_fmt(NULL, "Effect %u twice", effect->etype);
|
||||||
|
*effects |= (1ULL << effect->etype);
|
||||||
|
|
||||||
|
switch (effect->etype) {
|
||||||
|
case STATE_EFFECT_new_state:
|
||||||
|
sdata->core.state = effect->u.new_state;
|
||||||
|
break;
|
||||||
|
case STATE_EFFECT_in_error:
|
||||||
|
break;
|
||||||
|
case STATE_EFFECT_broadcast_tx:
|
||||||
|
break;
|
||||||
|
case STATE_EFFECT_send_pkt: {
|
||||||
|
const char *pkt = (const char *)effect->u.send_pkt;
|
||||||
|
*output = effect->u.send_pkt;
|
||||||
|
|
||||||
/* Check for errors. */
|
/* 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)
|
|
||||||
return "packets still open after error pkt";
|
|
||||||
}
|
|
||||||
/* We can abandon and add a new one, if we're LOWPRIO */
|
|
||||||
if (effect->htlc_abandon) {
|
|
||||||
if (sdata->current_htlc.htlc.id == -1)
|
|
||||||
return "HTLC not in progress, can't abandon";
|
|
||||||
if (effect->htlc_fulfill)
|
|
||||||
return "Complete and abandon?";
|
|
||||||
sdata->current_htlc.htlc.id = -1;
|
|
||||||
}
|
|
||||||
if (effect->htlc_in_progress) {
|
|
||||||
if (sdata->current_htlc.htlc.id != -1)
|
if (sdata->current_htlc.htlc.id != -1)
|
||||||
return "HTLC already in progress";
|
return "HTLC already in progress";
|
||||||
if (effect->htlc_fulfill)
|
sdata->current_htlc = *effect->u.htlc_in_progress;
|
||||||
return "Complete in one step?";
|
break;
|
||||||
sdata->current_htlc = *effect->htlc_in_progress;
|
case STATE_EFFECT_update_theirsig:
|
||||||
}
|
break;
|
||||||
if (effect->htlc_fulfill) {
|
case STATE_EFFECT_htlc_abandon:
|
||||||
|
if (sdata->current_htlc.htlc.id == -1)
|
||||||
|
return "HTLC not in progress, can't abandon";
|
||||||
|
sdata->current_htlc.htlc.id = -1;
|
||||||
|
break;
|
||||||
|
case STATE_EFFECT_htlc_fulfill:
|
||||||
if (sdata->current_htlc.htlc.id == -1)
|
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(©, &peer, sdata);
|
copy_peers(©, &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, ©, i, idata, effect);
|
effect = state(ctx, copy.core.state, ©, i, idata);
|
||||||
|
|
||||||
|
newstate = get_state_effect(effect, sdata->core.state);
|
||||||
|
|
||||||
normalpath &= normal_path(i, sdata->core.state, newstate);
|
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(©, effect, &output);
|
||||||
|
update_trail(&t, ©, output);
|
||||||
problem = apply_effects(©, effect);
|
|
||||||
update_trail(&t, ©, 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(©))
|
if (outstanding_htlc_watches(©))
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user