mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
daemon: allow commands during commit.
There's no real reason to avoid commands for the next commit; this has the benefit that we can remove the infrastructure to queue commands. The only exceptions are the commit command and the opening phase. We still only allow one commit at a time, but that's mainly run off a timer which can try again later. For the JSONRPC API used for testing, we can simply fail the commit if one is in progress. For opening we add an explicit peer_open_complete() call in place of using the command infrastructure. Commands are now outside the state machine altogether: we simply have it return the new state instead of the command status. The JSONRPC functions can also now run commands directly. This removes the idea of "peercond" as well: you can simply examine the states to determine whether an input is valid. There are fine-grained helpers for this now, too. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
d4862938c8
commit
4110376e87
522
daemon/peer.c
522
daemon/peer.c
@ -90,132 +90,135 @@ static struct json_result *null_response(const tal_t *ctx)
|
||||
return response;
|
||||
}
|
||||
|
||||
static void complete_json_command(struct command *jsoncmd,
|
||||
enum command_status status)
|
||||
/* FIXME: reorder to avoid predecls. */
|
||||
static bool peer_uncommitted_changes(const struct peer *peer);
|
||||
static void state_event(struct peer *peer,
|
||||
const enum state_input input,
|
||||
const union input *idata);
|
||||
|
||||
void peer_update_complete(struct peer *peer, const char *problem)
|
||||
{
|
||||
if (jsoncmd) {
|
||||
if (status == CMD_FAIL)
|
||||
/* FIXME: y'know, details. */
|
||||
command_fail(jsoncmd, "Failed");
|
||||
else {
|
||||
assert(status == CMD_SUCCESS);
|
||||
command_success(jsoncmd, null_response(jsoncmd));
|
||||
}
|
||||
if (!problem) {
|
||||
log_debug(peer->log, "peer_update_complete");
|
||||
if (peer->commit_jsoncmd)
|
||||
command_success(peer->commit_jsoncmd,
|
||||
null_response(peer->commit_jsoncmd));
|
||||
} else {
|
||||
log_unusual(peer->log, "peer_update_complete failed: %s",
|
||||
problem);
|
||||
if (peer->commit_jsoncmd)
|
||||
command_fail(peer->commit_jsoncmd, "%s", problem);
|
||||
}
|
||||
|
||||
/* Simply unset it: it will free itself. */
|
||||
peer->commit_jsoncmd = NULL;
|
||||
|
||||
/* Have we got more changes in the meantime? */
|
||||
if (peer_uncommitted_changes(peer)) {
|
||||
log_debug(peer->log, "peer_update_complete: more changes!");
|
||||
remote_changes_pending(peer);
|
||||
}
|
||||
}
|
||||
|
||||
static void peer_cmd_complete(struct peer *peer, enum command_status status)
|
||||
void peer_open_complete(struct peer *peer, const char *problem)
|
||||
{
|
||||
complete_json_command(peer->curr_cmd.jsoncmd, status);
|
||||
peer->curr_cmd.jsoncmd = NULL;
|
||||
peer->curr_cmd.cmd = INPUT_NONE;
|
||||
if (problem)
|
||||
log_unusual(peer->log, "peer open failed: %s", problem);
|
||||
else
|
||||
log_debug(peer->log, "peer open complete");
|
||||
}
|
||||
|
||||
static void set_current_command(struct peer *peer,
|
||||
const enum state_input input,
|
||||
void *idata,
|
||||
struct command *jsoncmd)
|
||||
{
|
||||
assert(peer->curr_cmd.cmd == INPUT_NONE);
|
||||
assert(input != INPUT_NONE);
|
||||
|
||||
peer->curr_cmd.cmd = input;
|
||||
/* This is a union, so assign to any member. */
|
||||
peer->curr_cmd.cmddata.pkt = idata;
|
||||
peer->curr_cmd.jsoncmd = jsoncmd;
|
||||
}
|
||||
|
||||
void set_peer_state(struct peer *peer, enum state newstate, const char *caller)
|
||||
static void set_peer_state(struct peer *peer, enum state newstate,
|
||||
const char *caller)
|
||||
{
|
||||
log_debug(peer->log, "%s: %s => %s", caller,
|
||||
state_name(peer->state), state_name(newstate));
|
||||
peer->state = newstate;
|
||||
}
|
||||
|
||||
/* FIXME: Flatten into do_commit. */
|
||||
static void command_send_commit(struct peer *peer, struct command *jsoncmd)
|
||||
{
|
||||
assert(peer->curr_cmd.cmd == INPUT_NONE);
|
||||
|
||||
/* Commands should still be blocked during this! */
|
||||
assert(peer->state != STATE_CLEARING_COMMITTING);
|
||||
|
||||
assert(state_can_commit(peer->state));
|
||||
assert(!peer->commit_jsoncmd);
|
||||
|
||||
peer->commit_jsoncmd = jsoncmd;
|
||||
if (peer->state == STATE_CLEARING) {
|
||||
peer->cond = PEER_BUSY;
|
||||
peer->curr_cmd.jsoncmd = jsoncmd;
|
||||
queue_pkt_commit(peer);
|
||||
set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__);
|
||||
} else {
|
||||
/* You can send commands if closing; peer->cond == CLOSING */
|
||||
assert(!state_is_closing(peer->state));
|
||||
set_current_command(peer, CMD_SEND_COMMIT, NULL, jsoncmd);
|
||||
assert(peer->state == STATE_NORMAL);
|
||||
state_event(peer, CMD_SEND_COMMIT, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_htlc_command(struct peer *peer,
|
||||
struct command *jsoncmd,
|
||||
enum state_input cmd,
|
||||
const union htlc_staging *stage)
|
||||
static bool command_htlc_fail(struct peer *peer, u64 id, struct command *jsoncmd)
|
||||
{
|
||||
/* FIXME: memleak! */
|
||||
/* FIXME: Get rid of struct htlc_progress */
|
||||
struct htlc_progress *progress = tal(peer, struct htlc_progress);
|
||||
|
||||
progress->stage = *stage;
|
||||
set_current_command(peer, cmd, progress, jsoncmd);
|
||||
}
|
||||
|
||||
static void command_htlc_fail(struct peer *peer, u64 id, struct command *jsoncmd)
|
||||
{
|
||||
assert(peer->curr_cmd.cmd == INPUT_NONE);
|
||||
|
||||
/* Commands should still be blocked during this! */
|
||||
assert(peer->state != STATE_CLEARING_COMMITTING);
|
||||
if (!state_can_remove_htlc(peer->state)) {
|
||||
if (jsoncmd)
|
||||
command_fail(jsoncmd,
|
||||
"htlc_fail not possible in state %s",
|
||||
state_name(peer->state));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (peer->state == STATE_CLEARING) {
|
||||
queue_pkt_htlc_fail(peer, id);
|
||||
complete_json_command(jsoncmd, CMD_SUCCESS);
|
||||
} else {
|
||||
union htlc_staging stage;
|
||||
union input idata;
|
||||
struct htlc_progress prog;
|
||||
|
||||
/* You can't send commands if closing; peer->cond == CLOSING */
|
||||
assert(!state_is_closing(peer->state));
|
||||
|
||||
stage.fail.fail = HTLC_FAIL;
|
||||
stage.fail.id = id;
|
||||
set_htlc_command(peer, jsoncmd, CMD_SEND_HTLC_FAIL, &stage);
|
||||
prog.stage.fail.fail = HTLC_FAIL;
|
||||
prog.stage.fail.id = id;
|
||||
/* FIXME: get rid of htlc_progress, just use htlc_staging. */
|
||||
idata.htlc_prog = &prog;
|
||||
state_event(peer, CMD_SEND_HTLC_FAIL, &idata);
|
||||
}
|
||||
|
||||
if (jsoncmd)
|
||||
command_success(jsoncmd, null_response(jsoncmd));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void command_htlc_fulfill(struct peer *peer,
|
||||
/* FIXME: Flatten into json_fulfillhtlc. */
|
||||
static bool command_htlc_fulfill(struct peer *peer,
|
||||
u64 id,
|
||||
const struct sha256 *r,
|
||||
struct command *jsoncmd)
|
||||
{
|
||||
assert(peer->curr_cmd.cmd == INPUT_NONE);
|
||||
if (!state_can_remove_htlc(peer->state)) {
|
||||
if (jsoncmd)
|
||||
command_fail(jsoncmd,
|
||||
"htlc_fulfill not possible in state %s",
|
||||
state_name(peer->state));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Commands should still be blocked during this! */
|
||||
assert(peer->state != STATE_CLEARING_COMMITTING);
|
||||
|
||||
if (peer->state == STATE_CLEARING) {
|
||||
queue_pkt_htlc_fulfill(peer, id, r);
|
||||
complete_json_command(jsoncmd, CMD_SUCCESS);
|
||||
} else {
|
||||
union htlc_staging stage;
|
||||
union input idata;
|
||||
struct htlc_progress prog;
|
||||
|
||||
/* You can't send commands if closing; peer->cond == CLOSING */
|
||||
assert(!state_is_closing(peer->state));
|
||||
|
||||
stage.fulfill.fulfill = HTLC_FULFILL;
|
||||
stage.fulfill.r = *r;
|
||||
stage.fulfill.id = id;
|
||||
set_htlc_command(peer, jsoncmd, CMD_SEND_HTLC_FULFILL, &stage);
|
||||
prog.stage.fulfill.fulfill = HTLC_FULFILL;
|
||||
prog.stage.fulfill.r = *r;
|
||||
prog.stage.fulfill.id = id;
|
||||
/* FIXME: get rid of htlc_progress, just use htlc_staging. */
|
||||
idata.htlc_prog = &prog;
|
||||
state_event(peer, CMD_SEND_HTLC_FULFILL, &idata);
|
||||
}
|
||||
|
||||
if (jsoncmd)
|
||||
command_success(jsoncmd, null_response(jsoncmd));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void peer_breakdown(struct peer *peer)
|
||||
{
|
||||
peer->cond = PEER_CLOSED;
|
||||
|
||||
/* If we have a closing tx, use it. */
|
||||
if (peer->closing.their_sig) {
|
||||
log_unusual(peer->log, "Peer breakdown: sending close tx");
|
||||
@ -279,7 +282,6 @@ static bool committed_to_htlcs(const struct peer *peer)
|
||||
static struct io_plan *peer_close(struct io_conn *conn, struct peer *peer)
|
||||
{
|
||||
/* Tell writer to wrap it up (may have to xmit first) */
|
||||
peer->cond = PEER_CLOSED;
|
||||
io_wake(peer);
|
||||
/* We do nothing more. */
|
||||
return io_wait(conn, NULL, io_never, NULL);
|
||||
@ -419,9 +421,6 @@ static struct io_plan *closing_pkt_in(struct io_conn *conn, struct peer *peer)
|
||||
return peer_read_packet(conn, peer, closing_pkt_in);
|
||||
}
|
||||
|
||||
/* FIXME: Predecls are bad, move up! */
|
||||
static void try_command(struct peer *peer);
|
||||
|
||||
/* This is the io loop while we're clearing. */
|
||||
static struct io_plan *clearing_pkt_in(struct io_conn *conn, struct peer *peer)
|
||||
{
|
||||
@ -438,11 +437,9 @@ static struct io_plan *clearing_pkt_in(struct io_conn *conn, struct peer *peer)
|
||||
err = accept_pkt_revocation(peer, pkt);
|
||||
if (!err) {
|
||||
set_peer_state(peer, STATE_CLEARING, __func__);
|
||||
peer_cmd_complete(peer, CMD_SUCCESS);
|
||||
peer->cond = PEER_CMD_OK;
|
||||
/* In case command is queued. */
|
||||
try_command(peer);
|
||||
}
|
||||
peer_update_complete(peer, NULL);
|
||||
} else
|
||||
peer_update_complete(peer, "bad revocation");
|
||||
}
|
||||
break;
|
||||
|
||||
@ -540,24 +537,12 @@ static void state_single(struct peer *peer,
|
||||
const enum state_input input,
|
||||
const union input *idata)
|
||||
{
|
||||
enum command_status status;
|
||||
const struct bitcoin_tx *broadcast;
|
||||
enum state newstate;
|
||||
size_t old_outpkts = tal_count(peer->outpkt);
|
||||
|
||||
status = state(peer, input, idata, &broadcast);
|
||||
log_debug(peer->log, "Peer condition: %s", cstatus_name(peer->cond));
|
||||
switch (status) {
|
||||
case CMD_NONE:
|
||||
break;
|
||||
case CMD_SUCCESS:
|
||||
log_add(peer->log, " (command success)");
|
||||
peer_cmd_complete(peer, CMD_SUCCESS);
|
||||
break;
|
||||
case CMD_FAIL:
|
||||
log_add(peer->log, " (command FAIL)");
|
||||
peer_cmd_complete(peer, CMD_FAIL);
|
||||
break;
|
||||
}
|
||||
newstate = state(peer, input, idata, &broadcast);
|
||||
set_peer_state(peer, newstate, input_name(input));
|
||||
|
||||
/* If we added uncommitted changes, we should have set them to send. */
|
||||
if (peer_uncommitted_changes(peer))
|
||||
@ -570,22 +555,18 @@ static void state_single(struct peer *peer,
|
||||
if (broadcast)
|
||||
broadcast_tx(peer, broadcast);
|
||||
|
||||
if (peer->state == STATE_ERR_BREAKDOWN)
|
||||
if (peer->state == STATE_CLEARING
|
||||
|| peer->state == STATE_CLEARING_COMMITTING) {
|
||||
peer_start_clearing(peer);
|
||||
} else if (state_is_error(peer->state)) {
|
||||
/* Breakdown is common, others less so. */
|
||||
if (peer->state != STATE_ERR_BREAKDOWN)
|
||||
log_broken(peer->log, "Entered error state %s",
|
||||
state_name(peer->state));
|
||||
peer_breakdown(peer);
|
||||
|
||||
/* Start output if not running already; it will close conn. */
|
||||
if (peer->cond == PEER_CLOSED)
|
||||
/* Start output if not running already; it will close conn. */
|
||||
io_wake(peer);
|
||||
|
||||
else if (peer->state == STATE_CLEARING
|
||||
|| peer->state == STATE_CLEARING_COMMITTING)
|
||||
peer_start_clearing(peer);
|
||||
|
||||
/* FIXME: Some of these should just result in this peer being killed? */
|
||||
else if (state_is_error(peer->state)) {
|
||||
log_broken(peer->log, "Entered error state %s",
|
||||
state_name(peer->state));
|
||||
fatal("Peer entered error state");
|
||||
}
|
||||
|
||||
/* Break out and free this peer if it's completely done. */
|
||||
@ -593,52 +574,6 @@ static void state_single(struct peer *peer,
|
||||
io_break(peer);
|
||||
}
|
||||
|
||||
static void try_command(struct peer *peer)
|
||||
{
|
||||
/* If we can accept a command, and we have one queued, run it. */
|
||||
while (peer->cond == PEER_CMD_OK
|
||||
&& !list_empty(&peer->pending_cmd)) {
|
||||
struct pending_cmd *pend = list_pop(&peer->pending_cmd,
|
||||
struct pending_cmd, list);
|
||||
|
||||
assert(peer->curr_cmd.cmd == INPUT_NONE);
|
||||
|
||||
/* This can fail to enqueue a command! */
|
||||
pend->dequeue(peer, pend->arg);
|
||||
tal_free(pend);
|
||||
|
||||
if (peer->curr_cmd.cmd != INPUT_NONE) {
|
||||
state_single(peer, peer->curr_cmd.cmd,
|
||||
&peer->curr_cmd.cmddata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define queue_cmd(peer, cb, arg) \
|
||||
queue_cmd_((peer), \
|
||||
typesafe_cb_preargs(void, void *, \
|
||||
(cb), (arg), \
|
||||
struct peer *), \
|
||||
(arg))
|
||||
|
||||
static bool queue_cmd_(struct peer *peer,
|
||||
void (*dequeue)(struct peer *peer, void *arg),
|
||||
void *arg)
|
||||
{
|
||||
struct pending_cmd *pend;
|
||||
|
||||
if (peer->cond == PEER_CLOSED)
|
||||
return false;
|
||||
|
||||
pend = tal(peer, struct pending_cmd);
|
||||
pend->dequeue = dequeue;
|
||||
pend->arg = arg;
|
||||
|
||||
list_add_tail(&peer->pending_cmd, &pend->list);
|
||||
try_command(peer);
|
||||
return true;
|
||||
};
|
||||
|
||||
static void UNNEEDED queue_input(struct peer *peer,
|
||||
enum state_input input,
|
||||
const union input *idata)
|
||||
@ -657,7 +592,7 @@ static void state_event(struct peer *peer,
|
||||
{
|
||||
struct pending_input *pend;
|
||||
|
||||
if (state_is_closing(peer->state)) {
|
||||
if (!state_is_opening(peer->state) && !state_is_normal(peer->state)) {
|
||||
log_unusual(peer->log,
|
||||
"Unexpected input %s while state %s",
|
||||
input_name(input), state_name(peer->state));
|
||||
@ -670,8 +605,6 @@ static void state_event(struct peer *peer,
|
||||
state_event(peer, pend->input, &pend->idata);
|
||||
tal_free(pend);
|
||||
}
|
||||
|
||||
try_command(peer);
|
||||
}
|
||||
|
||||
static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer)
|
||||
@ -684,7 +617,7 @@ static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer)
|
||||
|
||||
if (n == 0) {
|
||||
/* We close the connection once we've sent everything. */
|
||||
if (peer->cond == PEER_CLOSED)
|
||||
if (!state_can_io(peer->state))
|
||||
return io_close(conn);
|
||||
return io_out_wait(conn, peer, pkt_out, peer);
|
||||
}
|
||||
@ -711,7 +644,7 @@ static struct io_plan *pkt_in(struct io_conn *conn, struct peer *peer)
|
||||
idata.pkt = tal_steal(ctx, peer->inpkt);
|
||||
|
||||
/* We ignore packets if they tell us to. */
|
||||
if (!peer->fake_close && peer->cond != PEER_CLOSED) {
|
||||
if (!peer->fake_close && state_can_io(peer->state)) {
|
||||
state_event(peer, peer->inpkt->pkt_case, &idata);
|
||||
}
|
||||
|
||||
@ -721,11 +654,6 @@ static struct io_plan *pkt_in(struct io_conn *conn, struct peer *peer)
|
||||
return peer_read_packet(conn, peer, pkt_in);
|
||||
}
|
||||
|
||||
static void do_anchor_offer(struct peer *peer, void *unused)
|
||||
{
|
||||
set_current_command(peer, peer->local.offer_anchor, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Crypto is on, we are live. */
|
||||
static struct io_plan *peer_crypto_on(struct io_conn *conn, struct peer *peer)
|
||||
{
|
||||
@ -735,8 +663,7 @@ static struct io_plan *peer_crypto_on(struct io_conn *conn, struct peer *peer)
|
||||
|
||||
assert(peer->state == STATE_INIT);
|
||||
|
||||
/* Using queue_cmd is overkill here, but it works. */
|
||||
queue_cmd(peer, do_anchor_offer, NULL);
|
||||
state_event(peer, peer->local.offer_anchor, NULL);
|
||||
|
||||
return io_duplex(conn,
|
||||
peer_read_packet(conn, peer, pkt_in),
|
||||
@ -768,13 +695,10 @@ static void peer_disconnect(struct io_conn *conn, struct peer *peer)
|
||||
io_break(peer);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: Try to reconnect. */
|
||||
/* This is an expected close. */
|
||||
if (peer->cond == PEER_CLOSED)
|
||||
return;
|
||||
|
||||
if (peer->state != STATE_ERR_BREAKDOWN) {
|
||||
/* This is an unexpected close. */
|
||||
if (!state_is_onchain(peer->state) && !state_is_error(peer->state)) {
|
||||
/* FIXME: Try to reconnect. */
|
||||
set_peer_state(peer, STATE_ERR_BREAKDOWN, "peer_disconnect");
|
||||
peer_breakdown(peer);
|
||||
}
|
||||
@ -783,11 +707,7 @@ static void peer_disconnect(struct io_conn *conn, struct peer *peer)
|
||||
static void do_commit(struct peer *peer, struct command *jsoncmd)
|
||||
{
|
||||
/* We can have changes we suggested, or changes they suggested. */
|
||||
if (peer->remote.staging_cstate
|
||||
&& peer->remote.commit
|
||||
&& peer->remote.commit->cstate
|
||||
&& peer->remote.staging_cstate->changes
|
||||
!= peer->remote.commit->cstate->changes) {
|
||||
if (peer_uncommitted_changes(peer)) {
|
||||
log_debug(peer->log, "do_commit: sending commit command");
|
||||
command_send_commit(peer, jsoncmd);
|
||||
return;
|
||||
@ -803,7 +723,17 @@ static void do_commit(struct peer *peer, struct command *jsoncmd)
|
||||
static void try_commit(struct peer *peer)
|
||||
{
|
||||
peer->commit_timer = NULL;
|
||||
queue_cmd(peer, do_commit, (struct command *)NULL);
|
||||
|
||||
if (state_can_commit(peer->state))
|
||||
do_commit(peer, NULL);
|
||||
else {
|
||||
/* FIXME: try again when we receive revocation, rather
|
||||
* than using timer! */
|
||||
log_debug(peer->log, "try_commit: state=%s, re-queueing timer",
|
||||
state_name(peer->state));
|
||||
|
||||
remote_changes_pending(peer);
|
||||
}
|
||||
}
|
||||
|
||||
void remote_changes_pending(struct peer *peer)
|
||||
@ -834,7 +764,6 @@ static struct peer *new_peer(struct lightningd_state *dstate,
|
||||
list_add(&dstate->peers, &peer->list);
|
||||
|
||||
peer->state = STATE_INIT;
|
||||
peer->cond = PEER_CMD_OK;
|
||||
peer->dstate = dstate;
|
||||
peer->addr.type = addr_type;
|
||||
peer->addr.protocol = addr_protocol;
|
||||
@ -842,7 +771,7 @@ static struct peer *new_peer(struct lightningd_state *dstate,
|
||||
peer->secrets = NULL;
|
||||
list_head_init(&peer->watches);
|
||||
peer->outpkt = tal_arr(peer, Pkt *, 0);
|
||||
peer->curr_cmd.cmd = INPUT_NONE;
|
||||
peer->commit_jsoncmd = NULL;
|
||||
list_head_init(&peer->pending_cmd);
|
||||
list_head_init(&peer->pending_input);
|
||||
list_head_init(&peer->outgoing_txs);
|
||||
@ -1832,7 +1761,10 @@ static void check_for_resolution(struct peer *peer,
|
||||
* *irrevocably resolved*.
|
||||
*/
|
||||
set_peer_state(peer, STATE_CLOSED, "check_for_resolution");
|
||||
io_break(peer);
|
||||
|
||||
/* It's theoretically possible that peer is still writing output */
|
||||
if (!peer->conn)
|
||||
io_break(peer);
|
||||
}
|
||||
|
||||
/* We assume the tx is valid! Don't do a blockchain.info and feed this
|
||||
@ -1843,6 +1775,8 @@ static void anchor_spent(struct peer *peer,
|
||||
void *unused)
|
||||
{
|
||||
struct sha256_double txid;
|
||||
Pkt *err;
|
||||
enum state newstate;
|
||||
|
||||
assert(input_num < tx->input_count);
|
||||
|
||||
@ -1850,22 +1784,6 @@ static void anchor_spent(struct peer *peer,
|
||||
if (input_num != 0)
|
||||
fatal("Anchor spend by non-single input tx");
|
||||
|
||||
/* BOLT #onchain:
|
||||
*
|
||||
* A node SHOULD fail the connection if it is not already
|
||||
* closed when it sees the funding transaction spent.
|
||||
*/
|
||||
if (peer->cond != PEER_CLOSED) {
|
||||
peer->cond = PEER_CLOSED;
|
||||
|
||||
/* BOLT #onchain:
|
||||
*
|
||||
* A node MAY send a descriptive error packet in this case.
|
||||
*/
|
||||
queue_pkt_err(peer,
|
||||
pkt_err(peer, "Funding transaction spent!"));
|
||||
}
|
||||
|
||||
/* We may have been following a different spend. Forget it. */
|
||||
reset_onchain_closing(peer);
|
||||
|
||||
@ -1875,25 +1793,23 @@ static void anchor_spent(struct peer *peer,
|
||||
peer->closing_onchain.ci = find_commit(peer->remote.commit, &txid);
|
||||
if (peer->closing_onchain.ci) {
|
||||
if (peer->closing_onchain.ci->revocation_preimage) {
|
||||
set_peer_state(peer, STATE_CLOSE_ONCHAIN_CHEATED,
|
||||
"anchor_spent");
|
||||
newstate = STATE_CLOSE_ONCHAIN_CHEATED;
|
||||
err = pkt_err(peer, "Revoked transaction seen");
|
||||
resolve_cheating(peer);
|
||||
} else {
|
||||
set_peer_state(peer,
|
||||
STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL,
|
||||
"anchor_spent");
|
||||
newstate = STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL;
|
||||
err = pkt_err(peer, "Unilateral close tx seen");
|
||||
resolve_their_unilateral(peer);
|
||||
}
|
||||
} else if (txidmatch(peer->local.commit->tx, &txid)) {
|
||||
set_peer_state(peer,
|
||||
STATE_CLOSE_ONCHAIN_OUR_UNILATERAL,
|
||||
"anchor_spent");
|
||||
newstate = STATE_CLOSE_ONCHAIN_OUR_UNILATERAL;
|
||||
/* We're almost certainly closed to them by now. */
|
||||
err = pkt_err(peer, "Our own unilateral close tx seen");
|
||||
peer->closing_onchain.ci = peer->local.commit;
|
||||
resolve_our_unilateral(peer);
|
||||
} else if (is_mutual_close(peer, tx)) {
|
||||
set_peer_state(peer,
|
||||
STATE_CLOSE_ONCHAIN_MUTUAL,
|
||||
"anchor_spent");
|
||||
newstate = STATE_CLOSE_ONCHAIN_MUTUAL;
|
||||
err = NULL;
|
||||
resolve_mutual_close(peer);
|
||||
} else {
|
||||
/* BOLT #onchain:
|
||||
@ -1914,6 +1830,26 @@ static void anchor_spent(struct peer *peer,
|
||||
peer->anchor.watches->depthok = INPUT_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* BOLT #onchain:
|
||||
*
|
||||
* A node MAY send a descriptive error packet in this case.
|
||||
*/
|
||||
if (err && state_can_io(peer->state))
|
||||
queue_pkt_err(peer, err);
|
||||
|
||||
set_peer_state(peer, newstate, "anchor_spent");
|
||||
|
||||
/* If we've just closed connection, make output close it. */
|
||||
io_wake(peer);
|
||||
|
||||
/* BOLT #onchain:
|
||||
*
|
||||
* A node SHOULD fail the connection if it is not already
|
||||
* closed when it sees the funding transaction spent.
|
||||
*/
|
||||
assert(!state_can_io(peer->state));
|
||||
|
||||
assert(peer->closing_onchain.resolved != NULL);
|
||||
watch_tx(tx, peer, tx, check_for_resolution, NULL);
|
||||
|
||||
@ -2355,7 +2291,6 @@ static void json_getpeers(struct command *cmd,
|
||||
json_object_start(response, NULL);
|
||||
json_add_string(response, "name", log_prefix(p->log));
|
||||
json_add_string(response, "state", state_name(p->state));
|
||||
json_add_string(response, "cmd", input_name(p->curr_cmd.cmd));
|
||||
|
||||
/* This is only valid after crypto setup. */
|
||||
if (p->state != STATE_INIT)
|
||||
@ -2407,10 +2342,12 @@ const struct json_command getpeers_command = {
|
||||
|
||||
/* FIXME: Make sure no HTLCs in any unrevoked commit tx are live. */
|
||||
|
||||
static void check_htlc_expiry(struct peer *peer, void *unused)
|
||||
static void check_htlc_expiry(struct peer *peer)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
log_debug(peer->log, "Expiry timedout!");
|
||||
|
||||
/* Check their currently still-existing htlcs for expiry:
|
||||
* We eliminate them from staging as we go. */
|
||||
for (i = 0; i < tal_count(peer->remote.staging_cstate->side[THEIRS].htlcs); i++) {
|
||||
@ -2425,17 +2362,12 @@ static void check_htlc_expiry(struct peer *peer, void *unused)
|
||||
< abs_locktime_to_seconds(&htlc->expiry))
|
||||
continue;
|
||||
|
||||
/* This can fail only if we're in an error state. */
|
||||
command_htlc_fail(peer, htlc->id, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void htlc_expiry_timeout(struct peer *peer)
|
||||
{
|
||||
log_debug(peer->log, "Expiry timedout!");
|
||||
queue_cmd(peer, check_htlc_expiry, NULL);
|
||||
}
|
||||
|
||||
void peer_add_htlc_expiry(struct peer *peer,
|
||||
const struct abs_locktime *expiry)
|
||||
{
|
||||
@ -2445,26 +2377,24 @@ void peer_add_htlc_expiry(struct peer *peer,
|
||||
absexpiry.ts.tv_sec = abs_locktime_to_seconds(expiry) + 30;
|
||||
absexpiry.ts.tv_nsec = 0;
|
||||
|
||||
new_abstimer(peer->dstate, peer, absexpiry, htlc_expiry_timeout, peer);
|
||||
new_abstimer(peer->dstate, peer, absexpiry, check_htlc_expiry, peer);
|
||||
}
|
||||
|
||||
struct newhtlc {
|
||||
struct channel_htlc htlc;
|
||||
struct command *jsoncmd;
|
||||
};
|
||||
|
||||
/* We do final checks just before we start command, as things may have
|
||||
* changed. */
|
||||
static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
|
||||
static void do_newhtlc(struct peer *peer,
|
||||
const struct channel_htlc *htlc,
|
||||
struct command *jsoncmd)
|
||||
{
|
||||
struct channel_state *cstate;
|
||||
union htlc_staging stage;
|
||||
union input idata;
|
||||
struct htlc_progress prog;
|
||||
|
||||
/* Now we can assign counter and guarantee uniqueness. */
|
||||
newhtlc->htlc.id = peer->htlc_id_counter;
|
||||
stage.add.add = HTLC_ADD;
|
||||
stage.add.htlc = newhtlc->htlc;
|
||||
|
||||
prog.stage.add.add = HTLC_ADD;
|
||||
prog.stage.add.htlc = *htlc;
|
||||
prog.stage.add.htlc.id = peer->htlc_id_counter;
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* A node MUST NOT add a HTLC if it would result in it
|
||||
@ -2472,16 +2402,12 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
|
||||
*/
|
||||
if (tal_count(peer->local.staging_cstate->side[OURS].htlcs) == 300
|
||||
|| tal_count(peer->remote.staging_cstate->side[OURS].htlcs) == 300) {
|
||||
command_fail(newhtlc->jsoncmd, "Too many HTLCs");
|
||||
command_fail(jsoncmd, "Too many HTLCs");
|
||||
return;
|
||||
}
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* A node MUST NOT send a `update_add_htlc` after a `close_clearing`
|
||||
*/
|
||||
if (state_is_closing(peer->state)) {
|
||||
command_fail(newhtlc->jsoncmd, "Channel closing, state %s",
|
||||
if (!state_can_add_htlc(peer->state)) {
|
||||
command_fail(jsoncmd, "Channel closing, state %s",
|
||||
state_name(peer->state));
|
||||
return;
|
||||
}
|
||||
@ -2491,25 +2417,25 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
|
||||
* A node MUST NOT offer `amount_msat` it cannot pay for in
|
||||
* both commitment transactions at the current `fee_rate`
|
||||
*/
|
||||
cstate = copy_funding(newhtlc, peer->remote.staging_cstate);
|
||||
if (!funding_add_htlc(cstate, newhtlc->htlc.msatoshis,
|
||||
&newhtlc->htlc.expiry, &newhtlc->htlc.rhash,
|
||||
newhtlc->htlc.id, OURS)) {
|
||||
command_fail(newhtlc->jsoncmd,
|
||||
cstate = copy_funding(jsoncmd, peer->remote.staging_cstate);
|
||||
if (!funding_add_htlc(cstate, htlc->msatoshis,
|
||||
&htlc->expiry, &htlc->rhash,
|
||||
htlc->id, OURS)) {
|
||||
command_fail(jsoncmd,
|
||||
"Cannot afford %"PRIu64
|
||||
" milli-satoshis in their commit tx",
|
||||
newhtlc->htlc.msatoshis);
|
||||
htlc->msatoshis);
|
||||
return;
|
||||
}
|
||||
|
||||
cstate = copy_funding(newhtlc, peer->local.staging_cstate);
|
||||
if (!funding_add_htlc(cstate, newhtlc->htlc.msatoshis,
|
||||
&newhtlc->htlc.expiry, &newhtlc->htlc.rhash,
|
||||
newhtlc->htlc.id, OURS)) {
|
||||
command_fail(newhtlc->jsoncmd,
|
||||
cstate = copy_funding(jsoncmd, peer->local.staging_cstate);
|
||||
if (!funding_add_htlc(cstate, htlc->msatoshis,
|
||||
&htlc->expiry, &htlc->rhash,
|
||||
htlc->id, OURS)) {
|
||||
command_fail(jsoncmd,
|
||||
"Cannot afford %"PRIu64
|
||||
" milli-satoshis in our commit tx",
|
||||
newhtlc->htlc.msatoshis);
|
||||
htlc->msatoshis);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2517,7 +2443,9 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
|
||||
peer->htlc_id_counter++;
|
||||
|
||||
/* FIXME: Never propose duplicate rvalues? */
|
||||
set_htlc_command(peer, newhtlc->jsoncmd, CMD_SEND_HTLC_ADD, &stage);
|
||||
idata.htlc_prog = &prog;
|
||||
state_event(peer, CMD_SEND_HTLC_ADD, &idata);
|
||||
command_success(jsoncmd, null_response(jsoncmd));
|
||||
}
|
||||
|
||||
static void json_newhtlc(struct command *cmd,
|
||||
@ -2526,7 +2454,7 @@ static void json_newhtlc(struct command *cmd,
|
||||
struct peer *peer;
|
||||
jsmntok_t *peeridtok, *msatoshistok, *expirytok, *rhashtok;
|
||||
unsigned int expiry;
|
||||
struct newhtlc *newhtlc;
|
||||
struct channel_htlc htlc;
|
||||
|
||||
if (!json_get_params(buffer, params,
|
||||
"peerid", &peeridtok,
|
||||
@ -2549,11 +2477,7 @@ static void json_newhtlc(struct command *cmd,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attach to cmd until it's complete. */
|
||||
newhtlc = tal(cmd, struct newhtlc);
|
||||
newhtlc->jsoncmd = cmd;
|
||||
|
||||
if (!json_tok_u64(buffer, msatoshistok, &newhtlc->htlc.msatoshis)) {
|
||||
if (!json_tok_u64(buffer, msatoshistok, &htlc.msatoshis)) {
|
||||
command_fail(cmd, "'%.*s' is not a valid number",
|
||||
(int)(msatoshistok->end - msatoshistok->start),
|
||||
buffer + msatoshistok->start);
|
||||
@ -2566,20 +2490,20 @@ static void json_newhtlc(struct command *cmd,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!seconds_to_abs_locktime(expiry, &newhtlc->htlc.expiry)) {
|
||||
if (!seconds_to_abs_locktime(expiry, &htlc.expiry)) {
|
||||
command_fail(cmd, "'%.*s' is not a valid number",
|
||||
(int)(expirytok->end - expirytok->start),
|
||||
buffer + expirytok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (abs_locktime_to_seconds(&newhtlc->htlc.expiry) <
|
||||
if (abs_locktime_to_seconds(&htlc.expiry) <
|
||||
controlled_time().ts.tv_sec + peer->dstate->config.min_expiry) {
|
||||
command_fail(cmd, "HTLC expiry too soon!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (abs_locktime_to_seconds(&newhtlc->htlc.expiry) >
|
||||
if (abs_locktime_to_seconds(&htlc.expiry) >
|
||||
controlled_time().ts.tv_sec + peer->dstate->config.max_expiry) {
|
||||
command_fail(cmd, "HTLC expiry too far!");
|
||||
return;
|
||||
@ -2587,16 +2511,15 @@ static void json_newhtlc(struct command *cmd,
|
||||
|
||||
if (!hex_decode(buffer + rhashtok->start,
|
||||
rhashtok->end - rhashtok->start,
|
||||
&newhtlc->htlc.rhash,
|
||||
sizeof(newhtlc->htlc.rhash))) {
|
||||
&htlc.rhash,
|
||||
sizeof(htlc.rhash))) {
|
||||
command_fail(cmd, "'%.*s' is not a valid sha256 hash",
|
||||
(int)(rhashtok->end - rhashtok->start),
|
||||
buffer + rhashtok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue_cmd(peer, do_newhtlc, newhtlc))
|
||||
command_fail(cmd, "Peer closing");
|
||||
do_newhtlc(peer, &htlc, cmd);
|
||||
}
|
||||
|
||||
/* FIXME: Use HTLC ids, not r values! */
|
||||
@ -2618,27 +2541,24 @@ static size_t find_their_committed_htlc(struct peer *peer,
|
||||
return funding_find_htlc(peer->remote.staging_cstate, rhash, THEIRS);
|
||||
}
|
||||
|
||||
struct fulfillhtlc {
|
||||
struct command *jsoncmd;
|
||||
struct sha256 r;
|
||||
};
|
||||
|
||||
static void do_fullfill(struct peer *peer,
|
||||
struct fulfillhtlc *fulfillhtlc)
|
||||
const struct sha256 *r,
|
||||
struct command *jsoncmd)
|
||||
{
|
||||
struct sha256 rhash;
|
||||
size_t i;
|
||||
u64 id;
|
||||
|
||||
sha256(&rhash, &fulfillhtlc->r, sizeof(fulfillhtlc->r));
|
||||
sha256(&rhash, r, sizeof(*r));
|
||||
|
||||
i = find_their_committed_htlc(peer, &rhash);
|
||||
if (i == -1) {
|
||||
command_fail(fulfillhtlc->jsoncmd, "preimage htlc not found");
|
||||
command_fail(jsoncmd, "preimage htlc not found");
|
||||
return;
|
||||
}
|
||||
|
||||
id = peer->remote.staging_cstate->side[THEIRS].htlcs[i].id;
|
||||
command_htlc_fulfill(peer, id, &fulfillhtlc->r, fulfillhtlc->jsoncmd);
|
||||
command_htlc_fulfill(peer, id, r, jsoncmd);
|
||||
}
|
||||
|
||||
static void json_fulfillhtlc(struct command *cmd,
|
||||
@ -2646,7 +2566,7 @@ static void json_fulfillhtlc(struct command *cmd,
|
||||
{
|
||||
struct peer *peer;
|
||||
jsmntok_t *peeridtok, *rtok;
|
||||
struct fulfillhtlc *fulfillhtlc;
|
||||
struct sha256 r;
|
||||
|
||||
if (!json_get_params(buffer, params,
|
||||
"peerid", &peeridtok,
|
||||
@ -2667,20 +2587,15 @@ static void json_fulfillhtlc(struct command *cmd,
|
||||
return;
|
||||
}
|
||||
|
||||
fulfillhtlc = tal(cmd, struct fulfillhtlc);
|
||||
fulfillhtlc->jsoncmd = cmd;
|
||||
|
||||
if (!hex_decode(buffer + rtok->start,
|
||||
rtok->end - rtok->start,
|
||||
&fulfillhtlc->r, sizeof(fulfillhtlc->r))) {
|
||||
&r, sizeof(r))) {
|
||||
command_fail(cmd, "'%.*s' is not a valid sha256 preimage",
|
||||
(int)(rtok->end - rtok->start),
|
||||
buffer + rtok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue_cmd(peer, do_fullfill, fulfillhtlc))
|
||||
command_fail(cmd, "Peer closing");
|
||||
do_fullfill(peer, &r, cmd);
|
||||
}
|
||||
|
||||
const struct json_command fulfillhtlc_command = {
|
||||
@ -2690,26 +2605,23 @@ const struct json_command fulfillhtlc_command = {
|
||||
"Returns an empty result on success"
|
||||
};
|
||||
|
||||
struct failhtlc {
|
||||
struct command *jsoncmd;
|
||||
struct sha256 rhash;
|
||||
};
|
||||
|
||||
/* FIXME: Flatten into json_failhtlc. */
|
||||
static void do_failhtlc(struct peer *peer,
|
||||
struct failhtlc *failhtlc)
|
||||
const struct sha256 *rhash,
|
||||
struct command *jsoncmd)
|
||||
{
|
||||
size_t i;
|
||||
u64 id;
|
||||
|
||||
/* Look in peer->remote.staging_cstate->a, as that's where we'll
|
||||
* immediately remove it from: avoids double-handling. */
|
||||
i = find_their_committed_htlc(peer, &failhtlc->rhash);
|
||||
i = find_their_committed_htlc(peer, rhash);
|
||||
if (i == -1) {
|
||||
command_fail(failhtlc->jsoncmd, "htlc not found");
|
||||
command_fail(jsoncmd, "htlc not found");
|
||||
return;
|
||||
}
|
||||
id = peer->remote.staging_cstate->side[THEIRS].htlcs[i].id;
|
||||
command_htlc_fail(peer, id, failhtlc->jsoncmd);
|
||||
command_htlc_fail(peer, id, jsoncmd);
|
||||
}
|
||||
|
||||
static void json_failhtlc(struct command *cmd,
|
||||
@ -2717,7 +2629,7 @@ static void json_failhtlc(struct command *cmd,
|
||||
{
|
||||
struct peer *peer;
|
||||
jsmntok_t *peeridtok, *rhashtok;
|
||||
struct failhtlc *failhtlc;
|
||||
struct sha256 rhash;
|
||||
|
||||
if (!json_get_params(buffer, params,
|
||||
"peerid", &peeridtok,
|
||||
@ -2738,20 +2650,16 @@ static void json_failhtlc(struct command *cmd,
|
||||
return;
|
||||
}
|
||||
|
||||
failhtlc = tal(cmd, struct failhtlc);
|
||||
failhtlc->jsoncmd = cmd;
|
||||
|
||||
if (!hex_decode(buffer + rhashtok->start,
|
||||
rhashtok->end - rhashtok->start,
|
||||
&failhtlc->rhash, sizeof(failhtlc->rhash))) {
|
||||
&rhash, sizeof(rhash))) {
|
||||
command_fail(cmd, "'%.*s' is not a valid sha256 preimage",
|
||||
(int)(rhashtok->end - rhashtok->start),
|
||||
buffer + rhashtok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue_cmd(peer, do_failhtlc, failhtlc))
|
||||
command_fail(cmd, "Peer closing");
|
||||
do_failhtlc(peer, &rhash, cmd);
|
||||
}
|
||||
|
||||
const struct json_command failhtlc_command = {
|
||||
@ -2785,8 +2693,12 @@ static void json_commit(struct command *cmd,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue_cmd(peer, do_commit, cmd))
|
||||
command_fail(cmd, "Peer closing");
|
||||
if (!state_can_commit(peer->state)) {
|
||||
command_fail(cmd, "peer in state %s", state_name(peer->state));
|
||||
return;
|
||||
}
|
||||
|
||||
do_commit(peer, cmd);
|
||||
}
|
||||
|
||||
const struct json_command commit_command = {
|
||||
@ -2815,16 +2727,12 @@ static void json_close(struct command *cmd,
|
||||
return;
|
||||
}
|
||||
|
||||
if (state_is_closing(peer->state)) {
|
||||
if (!state_is_normal(peer->state) && !state_is_opening(peer->state)) {
|
||||
command_fail(cmd, "Peer is already closing: state %s",
|
||||
state_name(peer->state));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Closing causes any current command to fail. */
|
||||
if (peer->curr_cmd.cmd != INPUT_NONE)
|
||||
peer_cmd_complete(peer, CMD_FAIL);
|
||||
|
||||
if (peer->state == STATE_NORMAL_COMMITTING)
|
||||
set_peer_state(peer, STATE_CLEARING_COMMITTING, __func__);
|
||||
else
|
||||
|
@ -118,18 +118,11 @@ struct peer {
|
||||
/* State in state machine. */
|
||||
enum state state;
|
||||
|
||||
/* Condition of communications */
|
||||
enum state_peercond cond;
|
||||
|
||||
/* Network connection. */
|
||||
struct io_conn *conn;
|
||||
|
||||
/* Current command (or INPUT_NONE) */
|
||||
struct {
|
||||
enum state_input cmd;
|
||||
union input cmddata;
|
||||
struct command *jsoncmd;
|
||||
} curr_cmd;
|
||||
/* If we're doing a commit, this is the command which triggered it */
|
||||
struct command *commit_jsoncmd;
|
||||
|
||||
/* Pending inputs. */
|
||||
struct list_head pending_input;
|
||||
@ -234,8 +227,6 @@ void setup_listeners(struct lightningd_state *dstate, unsigned int portnum);
|
||||
/* Populates very first peer->{local,remote}.commit->{tx,cstate} */
|
||||
bool setup_first_commit(struct peer *peer);
|
||||
|
||||
void set_peer_state(struct peer *peer, enum state newstate, const char *why);
|
||||
|
||||
/* Set up timer: we have something we can commit. */
|
||||
void remote_changes_pending(struct peer *peer);
|
||||
|
||||
@ -243,6 +234,12 @@ void remote_changes_pending(struct peer *peer);
|
||||
void add_unacked(struct peer_visible_state *which,
|
||||
const union htlc_staging *stage);
|
||||
|
||||
/* Peer has recieved revocation, or problem (if non-NULL). */
|
||||
void peer_update_complete(struct peer *peer, const char *problem);
|
||||
|
||||
/* Peer has completed open, or problem (if non-NULL). */
|
||||
void peer_open_complete(struct peer *peer, const char *problem);
|
||||
|
||||
void peer_add_htlc_expiry(struct peer *peer,
|
||||
const struct abs_locktime *expiry);
|
||||
|
||||
|
@ -298,18 +298,16 @@ if [ -n "$TIMEOUT_ANCHOR" ]; then
|
||||
MOCKTIME=$((`date +%s` + 7200 + 3 * 1200 + 1))
|
||||
|
||||
lcli1 dev-mocktime $MOCKTIME
|
||||
lcli2 dev-mocktime $MOCKTIME
|
||||
|
||||
# This will crash immediately
|
||||
if $LCLI2 dev-mocktime $MOCKTIME 2> /dev/null; then
|
||||
echo Node2 did not crash >&2
|
||||
exit 1
|
||||
fi
|
||||
$FGREP 'Entered error state STATE_ERR_ANCHOR_TIMEOUT' $DIR2/crash.log
|
||||
# Node2 should have gone via STATE_ERR_ANCHOR_TIMEOUT, then closed.
|
||||
lcli2 getlog | grep STATE_ERR_ANCHOR_TIMEOUT
|
||||
check_no_peers lcli2
|
||||
|
||||
# Node1 should be disconnected.
|
||||
check_peerconnected lcli1 false
|
||||
|
||||
# It should send out commit tx; mine it.
|
||||
# Node1 should send out commit tx; mine it.
|
||||
check_tx_spend lcli1
|
||||
$CLI generate 1
|
||||
|
||||
@ -335,6 +333,7 @@ if [ -n "$TIMEOUT_ANCHOR" ]; then
|
||||
check_no_peers lcli1
|
||||
|
||||
lcli1 stop
|
||||
lcli2 stop
|
||||
all_ok
|
||||
fi
|
||||
|
||||
|
20
names.c
20
names.c
@ -23,26 +23,6 @@ const char *input_name(enum state_input in)
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *cstatus_name(enum command_status cstatus)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; enum_command_status_names[i].name; i++)
|
||||
if (enum_command_status_names[i].v == cstatus)
|
||||
return enum_command_status_names[i].name;
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *peercond_name(enum state_peercond peercond)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; enum_state_peercond_names[i].name; i++)
|
||||
if (enum_state_peercond_names[i].v == peercond)
|
||||
return enum_state_peercond_names[i].name;
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *pkt_name(Pkt__PktCase pkt)
|
||||
{
|
||||
size_t i;
|
||||
|
2
names.h
2
names.h
@ -6,7 +6,5 @@
|
||||
|
||||
const char *state_name(enum state s);
|
||||
const char *input_name(enum state_input in);
|
||||
const char *cstatus_name(enum command_status cstatus);
|
||||
const char *peercond_name(enum state_peercond peercond);
|
||||
const char *pkt_name(Pkt__PktCase pkt);
|
||||
#endif /* LIGHTNING_NAMES_H */
|
||||
|
207
state.c
207
state.c
@ -6,57 +6,25 @@
|
||||
#include <names.h>
|
||||
#include <state.h>
|
||||
|
||||
static enum command_status next_state(struct peer *peer,
|
||||
const enum state_input input,
|
||||
enum command_status cstatus,
|
||||
const enum state state)
|
||||
static enum state next_state(struct peer *peer,
|
||||
const enum state_input input,
|
||||
const enum state state)
|
||||
{
|
||||
assert(peer->state != state);
|
||||
set_peer_state(peer, state, input_name(input));
|
||||
return cstatus;
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple marker to note we don't update state.
|
||||
*
|
||||
* This happens in three cases:
|
||||
* - We're ignoring packets while closing.
|
||||
* - We stop watching an on-chain HTLC: we indicate that we want
|
||||
* INPUT_NO_MORE_HTLCS when we get the last one.
|
||||
* - HTLC add/remove in STATE_NORMAL.
|
||||
* This happens in normal state except when committing or closing.
|
||||
*/
|
||||
static enum command_status unchanged_state(const struct peer *peer,
|
||||
const enum state_input input,
|
||||
enum command_status cstatus)
|
||||
static enum state unchanged_state(const struct peer *peer,
|
||||
const enum state_input input)
|
||||
{
|
||||
log_debug(peer->log, "%s: %s unchanged",
|
||||
input_name(input), state_name(peer->state));
|
||||
return cstatus;
|
||||
}
|
||||
|
||||
static void change_peer_cond(struct peer *peer,
|
||||
enum state_peercond old,
|
||||
enum state_peercond new)
|
||||
{
|
||||
assert(peer->cond == old);
|
||||
peer->cond = new;
|
||||
}
|
||||
|
||||
static void complete_cmd(struct peer *peer, enum command_status *statusp,
|
||||
enum command_status status)
|
||||
{
|
||||
change_peer_cond(peer, PEER_BUSY, PEER_CMD_OK);
|
||||
*statusp = status;
|
||||
}
|
||||
|
||||
/* FIXME: We do this when a command succeeds instantly, and
|
||||
* state is unchanged. */
|
||||
static enum command_status instant_cmd_success(struct peer *peer,
|
||||
enum command_status cstatus)
|
||||
{
|
||||
assert(peer->cond == PEER_CMD_OK);
|
||||
assert(cstatus == CMD_NONE);
|
||||
return CMD_SUCCESS;
|
||||
return peer->state;
|
||||
}
|
||||
|
||||
static void queue_tx_broadcast(const struct bitcoin_tx **broadcast,
|
||||
@ -67,13 +35,12 @@ static void queue_tx_broadcast(const struct bitcoin_tx **broadcast,
|
||||
*broadcast = tx;
|
||||
}
|
||||
|
||||
enum command_status state(struct peer *peer,
|
||||
const enum state_input input,
|
||||
const union input *idata,
|
||||
const struct bitcoin_tx **broadcast)
|
||||
enum state state(struct peer *peer,
|
||||
const enum state_input input,
|
||||
const union input *idata,
|
||||
const struct bitcoin_tx **broadcast)
|
||||
{
|
||||
Pkt *err;
|
||||
enum command_status cstatus = CMD_NONE;
|
||||
|
||||
*broadcast = NULL;
|
||||
|
||||
@ -85,14 +52,12 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, CMD_OPEN_WITH_ANCHOR)) {
|
||||
queue_pkt_open(peer,
|
||||
OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR);
|
||||
change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY);
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAIT_FOR_OPEN_WITHANCHOR);
|
||||
} else if (input_is(input, CMD_OPEN_WITHOUT_ANCHOR)) {
|
||||
change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY);
|
||||
queue_pkt_open(peer,
|
||||
OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR);
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAIT_FOR_OPEN_NOANCHOR);
|
||||
}
|
||||
break;
|
||||
@ -100,12 +65,12 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, PKT_OPEN)) {
|
||||
err = accept_pkt_open(peer, idata->pkt);
|
||||
if (err) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, err->error->problem);
|
||||
goto err_breakdown;
|
||||
}
|
||||
return next_state(peer, input, cstatus, STATE_OPEN_WAIT_FOR_ANCHOR);
|
||||
return next_state(peer, input, STATE_OPEN_WAIT_FOR_ANCHOR);
|
||||
} else if (input_is_pkt(input)) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
@ -113,25 +78,25 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, PKT_OPEN)) {
|
||||
err = accept_pkt_open(peer, idata->pkt);
|
||||
if (err) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, err->error->problem);
|
||||
goto err_breakdown;
|
||||
}
|
||||
bitcoin_create_anchor(peer, BITCOIN_ANCHOR_CREATED);
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAIT_FOR_ANCHOR_CREATE);
|
||||
} else if (input_is_pkt(input)) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
case STATE_OPEN_WAIT_FOR_ANCHOR_CREATE:
|
||||
if (input_is(input, BITCOIN_ANCHOR_CREATED)) {
|
||||
queue_pkt_anchor(peer);
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAIT_FOR_COMMIT_SIG);
|
||||
} else if (input_is_pkt(input)) {
|
||||
bitcoin_release_anchor(peer, BITCOIN_ANCHOR_CREATED);
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
@ -139,7 +104,7 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, PKT_OPEN_ANCHOR)) {
|
||||
err = accept_pkt_anchor(peer, idata->pkt);
|
||||
if (err) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, err->error->problem);
|
||||
goto err_breakdown;
|
||||
}
|
||||
queue_pkt_open_commit_sig(peer);
|
||||
@ -147,10 +112,10 @@ enum command_status state(struct peer *peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
BITCOIN_ANCHOR_TIMEOUT);
|
||||
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAITING_THEIRANCHOR);
|
||||
} else if (input_is_pkt(input)) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
@ -159,18 +124,18 @@ enum command_status state(struct peer *peer,
|
||||
err = accept_pkt_open_commit_sig(peer, idata->pkt);
|
||||
if (err) {
|
||||
bitcoin_release_anchor(peer, INPUT_NONE);
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, err->error->problem);
|
||||
goto err_breakdown;
|
||||
}
|
||||
queue_tx_broadcast(broadcast, bitcoin_anchor(peer));
|
||||
peer_watch_anchor(peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
INPUT_NONE);
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAITING_OURANCHOR);
|
||||
} else if (input_is_pkt(input)) {
|
||||
bitcoin_release_anchor(peer, INPUT_NONE);
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
@ -178,14 +143,14 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, PKT_OPEN_COMPLETE)) {
|
||||
err = accept_pkt_open_complete(peer, idata->pkt);
|
||||
if (err) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, err->error->problem);
|
||||
/* We no longer care about anchor depth. */
|
||||
peer_unwatch_anchor_depth(peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
INPUT_NONE);
|
||||
goto err_breakdown;
|
||||
}
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED);
|
||||
}
|
||||
/* Fall thru */
|
||||
@ -193,24 +158,24 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) {
|
||||
queue_pkt_open_complete(peer);
|
||||
if (peer->state == STATE_OPEN_WAITING_OURANCHOR_THEYCOMPLETED) {
|
||||
complete_cmd(peer, &cstatus, CMD_SUCCESS);
|
||||
return next_state(peer, input, cstatus, STATE_NORMAL);
|
||||
peer_open_complete(peer, NULL);
|
||||
return next_state(peer, input, STATE_NORMAL);
|
||||
}
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR);
|
||||
} else if (input_is(input, PKT_CLOSE_CLEARING)) {
|
||||
/* We no longer care about anchor depth. */
|
||||
peer_unwatch_anchor_depth(peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
INPUT_NONE);
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "Received PKT_CLOSE_CLEARING");
|
||||
goto accept_clearing;
|
||||
} else if (input_is_pkt(input)) {
|
||||
/* We no longer care about anchor depth. */
|
||||
peer_unwatch_anchor_depth(peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
INPUT_NONE);
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
@ -218,14 +183,14 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, PKT_OPEN_COMPLETE)) {
|
||||
err = accept_pkt_open_complete(peer, idata->pkt);
|
||||
if (err) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, err->error->problem);
|
||||
/* We no longer care about anchor depth. */
|
||||
peer_unwatch_anchor_depth(peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
BITCOIN_ANCHOR_TIMEOUT);
|
||||
goto err_breakdown;
|
||||
}
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED);
|
||||
}
|
||||
/* Fall thru */
|
||||
@ -233,47 +198,42 @@ enum command_status state(struct peer *peer,
|
||||
if (input_is(input, BITCOIN_ANCHOR_TIMEOUT)) {
|
||||
/* Anchor didn't reach blockchain in reasonable time. */
|
||||
queue_pkt_err(peer, pkt_err(peer, "Anchor timed out"));
|
||||
return next_state(peer, input, cstatus, STATE_ERR_ANCHOR_TIMEOUT);
|
||||
return next_state(peer, input, STATE_ERR_ANCHOR_TIMEOUT);
|
||||
} else if (input_is(input, BITCOIN_ANCHOR_DEPTHOK)) {
|
||||
queue_pkt_open_complete(peer);
|
||||
if (peer->state == STATE_OPEN_WAITING_THEIRANCHOR_THEYCOMPLETED) {
|
||||
complete_cmd(peer, &cstatus, CMD_SUCCESS);
|
||||
return next_state(peer, input, cstatus, STATE_NORMAL);
|
||||
peer_open_complete(peer, NULL);
|
||||
return next_state(peer, input, STATE_NORMAL);
|
||||
}
|
||||
return next_state(peer, input, cstatus,
|
||||
return next_state(peer, input,
|
||||
STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR);
|
||||
} else if (input_is(input, PKT_CLOSE_CLEARING)) {
|
||||
/* We no longer care about anchor depth. */
|
||||
peer_unwatch_anchor_depth(peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
BITCOIN_ANCHOR_TIMEOUT);
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "Received PKT_CLOSE_CLEARING");
|
||||
goto accept_clearing;
|
||||
} else if (input_is_pkt(input)) {
|
||||
/* We no longer care about anchor depth. */
|
||||
peer_unwatch_anchor_depth(peer,
|
||||
BITCOIN_ANCHOR_DEPTHOK,
|
||||
BITCOIN_ANCHOR_TIMEOUT);
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
case STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR:
|
||||
case STATE_OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR:
|
||||
if (input_is(input, PKT_OPEN_COMPLETE)) {
|
||||
/* Ready for business! Anchorer goes first. */
|
||||
if (peer->state == STATE_OPEN_WAIT_FOR_COMPLETE_OURANCHOR) {
|
||||
complete_cmd(peer, &cstatus, CMD_SUCCESS);
|
||||
return next_state(peer, input, cstatus, STATE_NORMAL);
|
||||
} else {
|
||||
complete_cmd(peer, &cstatus, CMD_SUCCESS);
|
||||
return next_state(peer, input, cstatus, STATE_NORMAL);
|
||||
}
|
||||
/* Ready for business! */
|
||||
peer_open_complete(peer, NULL);
|
||||
return next_state(peer, input, STATE_NORMAL);
|
||||
} else if (input_is(input, PKT_CLOSE_CLEARING)) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "Received PKT_CLOSE_CLEARING");
|
||||
goto accept_clearing;
|
||||
} else if (input_is_pkt(input)) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
peer_open_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
@ -282,71 +242,66 @@ enum command_status state(struct peer *peer,
|
||||
* Channel normal operating states.
|
||||
*/
|
||||
case STATE_NORMAL:
|
||||
/*
|
||||
* FIXME: For simplicity, we disallow new staging requests
|
||||
* while a commit is outstanding.
|
||||
*/
|
||||
|
||||
/* You can only issue this command one at a time. */
|
||||
if (input_is(input, CMD_SEND_COMMIT)) {
|
||||
queue_pkt_commit(peer);
|
||||
change_peer_cond(peer, PEER_CMD_OK, PEER_BUSY);
|
||||
return next_state(peer, input, cstatus, STATE_NORMAL_COMMITTING);
|
||||
} else if (input_is(input, CMD_SEND_HTLC_ADD)) {
|
||||
return next_state(peer, input, STATE_NORMAL_COMMITTING);
|
||||
}
|
||||
/* Fall through... */
|
||||
case STATE_NORMAL_COMMITTING:
|
||||
if (input_is(input, CMD_SEND_HTLC_ADD)) {
|
||||
/* We are to send an HTLC add. */
|
||||
queue_pkt_htlc_add(peer, idata->htlc_prog);
|
||||
return instant_cmd_success(peer, cstatus);
|
||||
return unchanged_state(peer, input);
|
||||
} else if (input_is(input, CMD_SEND_HTLC_FULFILL)) {
|
||||
assert(idata->htlc_prog->stage.type == HTLC_FULFILL);
|
||||
/* We are to send an HTLC fulfill. */
|
||||
queue_pkt_htlc_fulfill(peer,
|
||||
idata->htlc_prog->stage.fulfill.id,
|
||||
&idata->htlc_prog->stage.fulfill.r);
|
||||
return instant_cmd_success(peer, cstatus);
|
||||
return unchanged_state(peer, input);
|
||||
} else if (input_is(input, CMD_SEND_HTLC_FAIL)) {
|
||||
assert(idata->htlc_prog->stage.type == HTLC_FAIL);
|
||||
/* We are to send an HTLC fail. */
|
||||
queue_pkt_htlc_fail(peer, idata->htlc_prog->stage.fail.id);
|
||||
return instant_cmd_success(peer, cstatus);
|
||||
return unchanged_state(peer, input);
|
||||
}
|
||||
/* Fall through... */
|
||||
case STATE_NORMAL_COMMITTING:
|
||||
/* Only expect revocation in STATE_NORMAL_COMMITTING */
|
||||
if (peer->state == STATE_NORMAL_COMMITTING
|
||||
&& input_is(input, PKT_UPDATE_REVOCATION)) {
|
||||
else if (peer->state == STATE_NORMAL_COMMITTING
|
||||
&& input_is(input, PKT_UPDATE_REVOCATION)) {
|
||||
err = accept_pkt_revocation(peer, idata->pkt);
|
||||
if (err) {
|
||||
complete_cmd(peer, &cstatus, CMD_FAIL);
|
||||
goto err_breakdown;
|
||||
}
|
||||
complete_cmd(peer, &cstatus, CMD_SUCCESS);
|
||||
return next_state(peer, input, cstatus, STATE_NORMAL);
|
||||
if (err)
|
||||
goto err_breakdown_maybe_committing;
|
||||
peer_update_complete(peer, NULL);
|
||||
return next_state(peer, input, STATE_NORMAL);
|
||||
}
|
||||
|
||||
if (input_is(input, PKT_UPDATE_ADD_HTLC)) {
|
||||
err = accept_pkt_htlc_add(peer, idata->pkt);
|
||||
if (err)
|
||||
goto err_breakdown;
|
||||
return unchanged_state(peer, input, cstatus);
|
||||
goto err_breakdown_maybe_committing;
|
||||
return unchanged_state(peer, input);
|
||||
} else if (input_is(input, PKT_UPDATE_FULFILL_HTLC)) {
|
||||
err = accept_pkt_htlc_fulfill(peer, idata->pkt);
|
||||
if (err)
|
||||
goto err_breakdown;
|
||||
return unchanged_state(peer, input, cstatus);
|
||||
goto err_breakdown_maybe_committing;
|
||||
return unchanged_state(peer, input);
|
||||
} else if (input_is(input, PKT_UPDATE_FAIL_HTLC)) {
|
||||
err = accept_pkt_htlc_fail(peer, idata->pkt);
|
||||
if (err)
|
||||
goto err_breakdown;
|
||||
return unchanged_state(peer, input, cstatus);
|
||||
goto err_breakdown_maybe_committing;
|
||||
return unchanged_state(peer, input);
|
||||
} else if (input_is(input, PKT_UPDATE_COMMIT)) {
|
||||
err = accept_pkt_commit(peer, idata->pkt);
|
||||
if (err)
|
||||
goto err_breakdown;
|
||||
goto err_breakdown_maybe_committing;
|
||||
queue_pkt_revocation(peer);
|
||||
return unchanged_state(peer, input, cstatus);
|
||||
return unchanged_state(peer, input);
|
||||
} else if (input_is(input, PKT_CLOSE_CLEARING)) {
|
||||
goto accept_clearing;
|
||||
} else if (input_is_pkt(input)) {
|
||||
if (peer->state == STATE_NORMAL_COMMITTING)
|
||||
peer_update_complete(peer, "unexpected packet");
|
||||
goto unexpected_pkt;
|
||||
}
|
||||
break;
|
||||
@ -365,11 +320,11 @@ enum command_status state(struct peer *peer,
|
||||
case STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL:
|
||||
case STATE_CLOSE_ONCHAIN_OUR_UNILATERAL:
|
||||
case STATE_CLOSE_ONCHAIN_MUTUAL:
|
||||
return next_state(peer, input, cstatus, STATE_ERR_INTERNAL);
|
||||
return next_state(peer, input, STATE_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
/* State machine should handle all possible states. */
|
||||
return next_state(peer, input, cstatus, STATE_ERR_INTERNAL);
|
||||
return next_state(peer, input, STATE_ERR_INTERNAL);
|
||||
|
||||
unexpected_pkt:
|
||||
peer_unexpected_pkt(peer, idata->pkt);
|
||||
@ -379,19 +334,23 @@ unexpected_pkt:
|
||||
goto breakdown;
|
||||
}
|
||||
err = pkt_err_unexpected(peer, idata->pkt);
|
||||
|
||||
goto err_breakdown;
|
||||
|
||||
err_breakdown_maybe_committing:
|
||||
if (peer->state == STATE_NORMAL_COMMITTING)
|
||||
peer_update_complete(peer, "bad packet");
|
||||
err_breakdown:
|
||||
queue_pkt_err(peer, err);
|
||||
breakdown:
|
||||
return next_state(peer, input, cstatus, STATE_ERR_BREAKDOWN);
|
||||
return next_state(peer, input, STATE_ERR_BREAKDOWN);
|
||||
|
||||
accept_clearing:
|
||||
err = accept_pkt_close_clearing(peer, idata->pkt);
|
||||
if (err)
|
||||
goto err_breakdown;
|
||||
goto err_breakdown_maybe_committing;
|
||||
|
||||
/* If we've sent commit, we're still waiting for it when clearing. */
|
||||
if (peer->state == STATE_NORMAL_COMMITTING)
|
||||
return next_state(peer, input, cstatus, STATE_CLEARING_COMMITTING);
|
||||
return next_state(peer, input, cstatus, STATE_CLEARING);
|
||||
return next_state(peer, input, STATE_CLEARING_COMMITTING);
|
||||
return next_state(peer, input, STATE_CLEARING);
|
||||
}
|
||||
|
61
state.h
61
state.h
@ -16,14 +16,61 @@
|
||||
|
||||
static inline bool state_is_error(enum state s)
|
||||
{
|
||||
return s >= STATE_ERR_ANCHOR_TIMEOUT && s <= STATE_ERR_INTERNAL;
|
||||
return s >= STATE_ERR_BREAKDOWN && s <= STATE_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
static inline bool state_is_closing(enum state s)
|
||||
static inline bool state_is_clearing(enum state s)
|
||||
{
|
||||
return s >= STATE_CLEARING;
|
||||
return s == STATE_CLEARING || s == STATE_CLEARING_COMMITTING;
|
||||
}
|
||||
|
||||
static inline bool state_is_onchain(enum state s)
|
||||
{
|
||||
return s >= STATE_CLOSE_ONCHAIN_CHEATED
|
||||
&& s <= STATE_CLOSE_ONCHAIN_MUTUAL;
|
||||
}
|
||||
|
||||
static inline bool state_is_normal(enum state s)
|
||||
{
|
||||
return s == STATE_NORMAL || s == STATE_NORMAL_COMMITTING;
|
||||
}
|
||||
|
||||
static inline bool state_is_opening(enum state s)
|
||||
{
|
||||
return s < STATE_NORMAL;
|
||||
}
|
||||
|
||||
static inline bool state_can_io(enum state s)
|
||||
{
|
||||
if (state_is_error(s))
|
||||
return false;
|
||||
if (s == STATE_CLOSED)
|
||||
return false;
|
||||
if (state_is_onchain(s))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool state_can_commit(enum state s)
|
||||
{
|
||||
return s == STATE_NORMAL || s == STATE_CLEARING;
|
||||
}
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* A node MUST NOT send a `update_add_htlc` after a `close_clearing`
|
||||
*/
|
||||
static inline bool state_can_add_htlc(enum state s)
|
||||
{
|
||||
return state_is_normal(s);
|
||||
}
|
||||
|
||||
static inline bool state_can_remove_htlc(enum state s)
|
||||
{
|
||||
return state_is_normal(s) || state_is_clearing(s);
|
||||
}
|
||||
|
||||
|
||||
struct peer;
|
||||
struct bitcoin_tx;
|
||||
|
||||
@ -48,10 +95,10 @@ union input {
|
||||
} *htlc_onchain;
|
||||
};
|
||||
|
||||
enum command_status state(struct peer *peer,
|
||||
const enum state_input input,
|
||||
const union input *idata,
|
||||
const struct bitcoin_tx **broadcast);
|
||||
enum state state(struct peer *peer,
|
||||
const enum state_input input,
|
||||
const union input *idata,
|
||||
const struct bitcoin_tx **broadcast);
|
||||
|
||||
/* Any CMD_SEND_HTLC_* */
|
||||
#define CMD_SEND_UPDATE_ANY INPUT_MAX
|
||||
|
@ -117,23 +117,4 @@ enum state_input {
|
||||
|
||||
INPUT_MAX
|
||||
};
|
||||
|
||||
enum state_peercond {
|
||||
/* Ready to accept a new command. */
|
||||
PEER_CMD_OK,
|
||||
/* Don't send me commands, I'm busy. */
|
||||
PEER_BUSY,
|
||||
/* No more packets, I'm closed. */
|
||||
PEER_CLOSED
|
||||
};
|
||||
|
||||
enum command_status {
|
||||
/* Nothing changed. */
|
||||
CMD_NONE,
|
||||
/* Command succeeded. */
|
||||
CMD_SUCCESS,
|
||||
/* Failed. */
|
||||
CMD_FAIL
|
||||
};
|
||||
|
||||
#endif /* LIGHTNING_STATE_TYPES_H */
|
||||
|
Loading…
Reference in New Issue
Block a user