diff --git a/lightningd/channel/channel.c b/lightningd/channel/channel.c index fec3ac8af..be157fef3 100644 --- a/lightningd/channel/channel.c +++ b/lightningd/channel/channel.c @@ -1303,6 +1303,32 @@ static void resend_revoke(struct peer *peer) msg_enqueue(&peer->peer_out, take(msg)); } +static void send_fail_or_fulfill(struct peer *peer, const struct htlc *h) +{ + u8 *msg; + if (h->malformed) { + struct sha256 sha256_of_onion; + sha256(&sha256_of_onion, h->routing, tal_len(h->routing)); + + msg = towire_update_fail_malformed_htlc(peer, &peer->channel_id, + h->id, &sha256_of_onion, + h->malformed); + } else if (h->fail) { + msg = towire_update_fail_htlc(peer, &peer->channel_id, h->id, + h->fail); + } else if (h->r) { + msg = towire_update_fulfill_htlc(peer, &peer->channel_id, h->id, + h->r); + } else + peer_failed(io_conn_fd(peer->peer_conn), + &peer->pcs.cs, + &peer->channel_id, + WIRE_CHANNEL_PEER_BAD_MESSAGE, + "HTLC %"PRIu64" state %s not failed/fulfilled", + h->id, htlc_state_name(h->state)); + msg_enqueue(&peer->peer_out, take(msg)); +} + static void resend_commitment(struct peer *peer, const struct changed_htlc *last) { size_t i; @@ -1344,31 +1370,7 @@ static void resend_commitment(struct peer *peer, const struct changed_htlc *last h->routing); msg_enqueue(&peer->peer_out, take(msg)); } else if (h->state == SENT_REMOVE_COMMIT) { - u8 *msg; - if (h->malformed) { - struct sha256 sha256_of_onion; - sha256(&sha256_of_onion, h->routing, - tal_len(h->routing)); - - msg = towire_update_fail_malformed_htlc - (peer, &peer->channel_id, h->id, - &sha256_of_onion, h->malformed); - } else if (h->fail) { - msg = towire_update_fail_htlc - (peer, &peer->channel_id, h->id, - h->fail); - } else if (h->r) { - msg = towire_update_fulfill_htlc - (peer, &peer->channel_id, h->id, - h->r); - } else - peer_failed(io_conn_fd(peer->peer_conn), - &peer->pcs.cs, - &peer->channel_id, - WIRE_CHANNEL_PEER_BAD_MESSAGE, - "HTLC %"PRIu64" state %s not failed/fulfilled", - h->id, htlc_state_name(h->state)); - msg_enqueue(&peer->peer_out, take(msg)); + send_fail_or_fulfill(peer, h); } } @@ -1389,6 +1391,8 @@ static struct io_plan *handle_peer_reestablish(struct io_conn *conn, u64 last_commitidx_sent, last_revokeidx_sent; u64 commitments_received, revocations_received; bool retransmit_revoke_and_ack; + struct htlc_map_iter it; + const struct htlc *htlc; if (gossip_msg(msg)) { /* Forward to gossip daemon */ @@ -1498,6 +1502,14 @@ static struct io_plan *handle_peer_reestablish(struct io_conn *conn, /* Start commit timer: if we sent revoke we might need it. */ start_commit_timer(peer); + /* Now, re-send any that we're supposed to be failing. */ + for (htlc = htlc_map_first(&peer->channel->htlcs, &it); + htlc; + htlc = htlc_map_next(&peer->channel->htlcs, &it)) { + if (htlc->state == SENT_REMOVE_HTLC) + send_fail_or_fulfill(peer, htlc); + } + /* Now into normal mode. */ return setup_peer_conn(conn, peer); } diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index ec89e722d..d34619b03 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -18,6 +18,59 @@ #include #include +static bool state_update_ok(struct peer *peer, + enum htlc_state oldstate, enum htlc_state newstate, + u64 htlc_id, const char *dir) +{ + enum htlc_state expected = oldstate + 1; + + /* We never get told about RCVD_REMOVE_HTLC, so skip over that + * (we initialize in SENT_ADD_HTLC / RCVD_ADD_COMMIT, so those + * work). */ + if (expected == RCVD_REMOVE_HTLC) + expected = RCVD_REMOVE_COMMIT; + + if (newstate != expected) { + peer_internal_error(peer, + "HTLC %s %"PRIu64" invalid update %s->%s", + dir, htlc_id, + htlc_state_name(oldstate), + htlc_state_name(newstate)); + return false; + } + + log_debug(peer->log, "HTLC %s %"PRIu64" %s->%s", + dir, htlc_id, + htlc_state_name(oldstate), htlc_state_name(newstate)); + return true; +} + +static bool htlc_in_update_state(struct peer *peer, + struct htlc_in *hin, + enum htlc_state newstate) +{ + if (!state_update_ok(peer, hin->hstate, newstate, hin->key.id, "in")) + return false; + + /* FIXME: db commit */ + hin->hstate = newstate; + htlc_in_check(hin, __func__); + return true; +} + +static bool htlc_out_update_state(struct peer *peer, + struct htlc_out *hout, + enum htlc_state newstate) +{ + if (!state_update_ok(peer, hout->hstate, newstate, hout->key.id, "out")) + return false; + + /* FIXME: db commit */ + hout->hstate = newstate; + htlc_out_check(hout, __func__); + return true; +} + /* This is where we write to the database the minimal HTLC info * required to do penalty transaction */ static void save_htlc_stub(struct lightningd *ld, @@ -29,9 +82,24 @@ static void save_htlc_stub(struct lightningd *ld, /* FIXME: remember peer, side, cltv and RIPEMD160(hash) */ } -/* This obfuscates the message, whether local or forwarded. */ -static void relay_htlc_failmsg(struct htlc_in *hin) +static void fail_in_htlc(struct htlc_in *hin, + enum onion_type malformed, + const u8 *failuremsg) { + assert(!hin->preimage); + if (malformed) + assert(!failuremsg); + else + assert(failuremsg); + + hin->malformed = malformed; + if (failuremsg) + hin->failuremsg = tal_dup_arr(hin, u8, failuremsg, tal_len(failuremsg), 0); + + /* We update state now to signal it's in progress, for persistence. */ + htlc_in_update_state(hin->key.peer, hin, SENT_REMOVE_HTLC); + + /* Tell peer, if we can. */ if (!hin->key.peer->owner) return; @@ -44,6 +112,7 @@ static void relay_htlc_failmsg(struct htlc_in *hin) } else { u8 *reply; + /* This obfuscates the message, whether local or forwarded. */ reply = wrap_onionreply(hin, &hin->shared_secret, hin->failuremsg); subd_send_msg(hin->key.peer->owner, @@ -104,13 +173,14 @@ static u8 *make_failmsg(const tal_t *ctx, u64 msatoshi, abort(); } -static void fail_htlc(struct htlc_in *hin, enum onion_type failcode) +/* This is used for cases where we can immediately fail the HTLC. */ +static void local_fail_htlc(struct htlc_in *hin, enum onion_type failcode) { log_info(hin->key.peer->log, "failed htlc %"PRIu64" code 0x%04x (%s)", hin->key.id, failcode, onion_type_name(failcode)); if (failcode & BADONION) - hin->malformed = failcode; + fail_in_htlc(hin, failcode, NULL); else { u8 *msg; @@ -119,12 +189,9 @@ static void fail_htlc(struct htlc_in *hin, enum onion_type failcode) } msg = make_failmsg(hin, hin->msatoshi, failcode, NULL); - hin->failuremsg = create_onionreply(hin, &hin->shared_secret, - msg); + fail_in_htlc(hin, 0, take(create_onionreply(hin, &hin->shared_secret, msg))); tal_free(msg); } - htlc_in_check(hin, __func__); - relay_htlc_failmsg(hin); } /* localfail are for handing to the local payer if it's local. */ @@ -132,17 +199,9 @@ static void fail_out_htlc(struct htlc_out *hout, const char *localfail) { htlc_out_check(hout, __func__); assert(hout->malformed || hout->failuremsg); + assert(!hout->malformed || !hout->failuremsg); if (hout->in) { - if (hout->in->malformed) - hout->malformed = hout->in->malformed; - else { - hout->in->failuremsg - = tal_dup_arr(hout->in, u8, - hout->failuremsg, - tal_len(hout->failuremsg), - 0); - } - relay_htlc_failmsg(hout->in); + fail_in_htlc(hout->in, hout->malformed, hout->failuremsg); } else { payment_failed(hout->key.peer->ld, hout, localfail); } @@ -212,6 +271,9 @@ static void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage) hin->preimage = tal_dup(hin, struct preimage, preimage); htlc_in_check(hin, __func__); + /* We update state now to signal it's in progress, for persistence. */ + htlc_in_update_state(hin->key.peer, hin, SENT_REMOVE_HTLC); + /* FIXME: fail the peer if it doesn't tell us that htlc fulfill is * committed before deadline. */ @@ -303,7 +365,7 @@ static void handle_localpay(struct htlc_in *hin, return; fail: - fail_htlc(hin, failcode); + local_fail_htlc(hin, failcode); } /* @@ -341,7 +403,7 @@ static bool rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds, } if (failure_code) { - fail_htlc(hout->in, failure_code); + local_fail_htlc(hout->in, failure_code); return true; } @@ -467,7 +529,7 @@ static void forward_htlc(struct htlc_in *hin, return; fail: - fail_htlc(hin, failcode); + local_fail_htlc(hin, failcode); } /* Temporary information, while we resolve the next hop */ @@ -494,7 +556,7 @@ static bool channel_resolve_reply(struct subd *gossip, const u8 *msg, } if (tal_count(nodes) == 0) { - fail_htlc(gr->hin, WIRE_UNKNOWN_NEXT_PEER); + local_fail_htlc(gr->hin, WIRE_UNKNOWN_NEXT_PEER); return true; } else if (tal_count(nodes) != 2) { log_broken(gossip->log, @@ -517,61 +579,6 @@ static bool channel_resolve_reply(struct subd *gossip, const u8 *msg, return true; } -static bool state_update_ok(struct peer *peer, - enum htlc_state oldstate, enum htlc_state newstate, - u64 htlc_id, const char *dir) -{ - enum htlc_state expected = oldstate + 1; - - /* We never get told about RCVD_REMOVE_HTLC or SENT_REMOVE_HTLC, so - * skip over those (we initialize in SENT_ADD_HTLC / RCVD_ADD_COMMIT, so - * those work). */ - if (expected == RCVD_REMOVE_HTLC) - expected = RCVD_REMOVE_COMMIT; - else if (expected == SENT_REMOVE_HTLC) - expected = SENT_REMOVE_COMMIT; - - if (newstate != expected) { - peer_internal_error(peer, - "HTLC %s %"PRIu64" invalid update %s->%s", - dir, htlc_id, - htlc_state_name(oldstate), - htlc_state_name(newstate)); - return false; - } - - log_debug(peer->log, "HTLC %s %"PRIu64" %s->%s", - dir, htlc_id, - htlc_state_name(oldstate), htlc_state_name(newstate)); - return true; -} - -static bool htlc_in_update_state(struct peer *peer, - struct htlc_in *hin, - enum htlc_state newstate) -{ - if (!state_update_ok(peer, hin->hstate, newstate, hin->key.id, "in")) - return false; - - /* FIXME: db commit */ - hin->hstate = newstate; - htlc_in_check(hin, __func__); - return true; -} - -static bool htlc_out_update_state(struct peer *peer, - struct htlc_out *hout, - enum htlc_state newstate) -{ - if (!state_update_ok(peer, hout->hstate, newstate, hout->key.id, "out")) - return false; - - /* FIXME: db commit */ - hout->hstate = newstate; - htlc_out_check(hout, __func__); - return true; -} - /* Everyone is committed to this htlc of theirs */ static bool peer_accepted_htlc(struct peer *peer, u64 id, @@ -1142,7 +1149,7 @@ int peer_got_revoke(struct peer *peer, const u8 *msg) continue; hin = find_htlc_in(&peer->ld->htlcs_in, peer, changed[i].id); - fail_htlc(hin, failcodes[i]); + local_fail_htlc(hin, failcodes[i]); } return 0; }