From 2bed246e105576573725cfc94dc36a0742860b92 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Nov 2017 15:34:20 +1030 Subject: [PATCH] channeld: generate error messages instead of having master do it. The master now hands channeld either an error code, and channeld generates the error message, or an error message relayed from another node to pass through. This doesn't fill in the channel_update yet: we need to wire up gossipd to give us that. Signed-off-by: Rusty Russell --- channeld/channel.c | 111 ++++++++++++++++++++++++++++-- channeld/channel_wire.csv | 2 + lightningd/htlc_end.h | 8 ++- lightningd/peer_htlcs.c | 138 +++++++++++--------------------------- 4 files changed, 154 insertions(+), 105 deletions(-) diff --git a/channeld/channel.c b/channeld/channel.c index 537d93752..f3a27ef40 100644 --- a/channeld/channel.c +++ b/channeld/channel.c @@ -1985,20 +1985,118 @@ static void handle_preimage(struct peer *peer, const u8 *inmsg) abort(); } +static u8 *foreign_channel_update(const tal_t *ctx, + struct peer *peer, + const struct short_channel_id *scid) +{ + /* FIXME! */ + return NULL; +} + +static u8 *make_failmsg(const tal_t *ctx, + struct peer *peer, + const struct htlc *htlc, + enum onion_type failcode, + const struct short_channel_id *scid) +{ + u8 *msg, *channel_update = NULL; + u32 cltv_expiry = abs_locktime_to_blocks(&htlc->expiry); + + switch (failcode) { + case WIRE_INVALID_REALM: + msg = towire_invalid_realm(ctx); + goto done; + case WIRE_TEMPORARY_NODE_FAILURE: + msg = towire_temporary_node_failure(ctx); + goto done; + case WIRE_PERMANENT_NODE_FAILURE: + msg = towire_permanent_node_failure(ctx); + goto done; + case WIRE_REQUIRED_NODE_FEATURE_MISSING: + msg = towire_required_node_feature_missing(ctx); + goto done; + case WIRE_TEMPORARY_CHANNEL_FAILURE: + channel_update = foreign_channel_update(ctx, peer, scid); + msg = towire_temporary_channel_failure(ctx, channel_update); + goto done; + case WIRE_CHANNEL_DISABLED: + msg = towire_channel_disabled(ctx); + goto done; + case WIRE_PERMANENT_CHANNEL_FAILURE: + msg = towire_permanent_channel_failure(ctx); + goto done; + case WIRE_REQUIRED_CHANNEL_FEATURE_MISSING: + msg = towire_required_channel_feature_missing(ctx); + goto done; + case WIRE_UNKNOWN_NEXT_PEER: + msg = towire_unknown_next_peer(ctx); + goto done; + case WIRE_AMOUNT_BELOW_MINIMUM: + channel_update = foreign_channel_update(ctx, peer, scid); + msg = towire_amount_below_minimum(ctx, htlc->msatoshi, + channel_update); + goto done; + case WIRE_FEE_INSUFFICIENT: + channel_update = foreign_channel_update(ctx, peer, scid); + msg = towire_fee_insufficient(ctx, htlc->msatoshi, + channel_update); + goto done; + case WIRE_INCORRECT_CLTV_EXPIRY: + channel_update = foreign_channel_update(ctx, peer, scid); + msg = towire_incorrect_cltv_expiry(ctx, cltv_expiry, + channel_update); + goto done; + case WIRE_EXPIRY_TOO_SOON: + channel_update = foreign_channel_update(ctx, peer, scid); + msg = towire_expiry_too_soon(ctx, channel_update); + goto done; + case WIRE_EXPIRY_TOO_FAR: + msg = towire_expiry_too_far(ctx); + goto done; + case WIRE_UNKNOWN_PAYMENT_HASH: + msg = towire_unknown_payment_hash(ctx); + goto done; + case WIRE_INCORRECT_PAYMENT_AMOUNT: + msg = towire_incorrect_payment_amount(ctx); + goto done; + case WIRE_FINAL_EXPIRY_TOO_SOON: + msg = towire_final_expiry_too_soon(ctx); + goto done; + case WIRE_FINAL_INCORRECT_CLTV_EXPIRY: + msg = towire_final_incorrect_cltv_expiry(ctx, cltv_expiry); + goto done; + case WIRE_FINAL_INCORRECT_HTLC_AMOUNT: + msg = towire_final_incorrect_htlc_amount(ctx, htlc->msatoshi); + goto done; + case WIRE_INVALID_ONION_VERSION: + case WIRE_INVALID_ONION_HMAC: + case WIRE_INVALID_ONION_KEY: + break; + } + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Asked to create failmsg %u (%s)", + failcode, onion_type_name(failcode)); + +done: + tal_free(channel_update); + return msg; +} + static void handle_fail(struct peer *peer, const u8 *inmsg) { u8 *msg; u64 id; u8 *errpkt; u16 failcode; + struct short_channel_id scid; enum channel_remove_err e; struct htlc *h; if (!fromwire_channel_fail_htlc(inmsg, inmsg, NULL, &id, &errpkt, - &failcode)) + &failcode, &scid)) master_badmsg(WIRE_CHANNEL_FAIL_HTLC, inmsg); - if ((failcode & BADONION) && tal_len(errpkt)) + if (failcode && tal_len(errpkt)) status_failed(STATUS_FAIL_MASTER_IO, "Invalid channel_fail_htlc: %s with errpkt?", onion_type_name(failcode)); @@ -2017,8 +2115,13 @@ static void handle_fail(struct peer *peer, const u8 *inmsg) id, &sha256_of_onion, failcode); } else { - u8 *reply = wrap_onionreply(inmsg, h->shared_secret, - errpkt); + u8 *reply; + + if (failcode) + errpkt = make_failmsg(inmsg, peer, h, + failcode, &scid); + reply = wrap_onionreply(inmsg, h->shared_secret, + errpkt); msg = towire_update_fail_htlc(peer, &peer->channel_id, id, reply); } diff --git a/channeld/channel_wire.csv b/channeld/channel_wire.csv index 190a9a7d5..2a328102d 100644 --- a/channeld/channel_wire.csv +++ b/channeld/channel_wire.csv @@ -97,6 +97,8 @@ channel_fail_htlc,,len,u16 channel_fail_htlc,,error_pkt,len*u8 # If it errcode is != 0, it's a local error, otherwise we're passing thru. channel_fail_htlc,,errcode,u16 +# If errcode & UPDATE, this says which outgoing channel failed. +channel_fail_htlc,,which_channel,struct short_channel_id # Ping/pong test. channel_ping,1011 diff --git a/lightningd/htlc_end.h b/lightningd/htlc_end.h index 7d8c497e5..c7f0cfcdf 100644 --- a/lightningd/htlc_end.h +++ b/lightningd/htlc_end.h @@ -35,11 +35,15 @@ struct htlc_in { /* If a local error, this is non-zero. */ enum onion_type failcode; - /* Either a remote error, or local error if !(failure & BADONION). */ + /* For a remote error. */ const u8 *failuremsg; + /* If failcode & UPDATE, this is the channel which failed. */ + struct short_channel_id failoutchannel; + /* If they fulfilled, here's the preimage. */ struct preimage *preimage; + }; struct htlc_out { @@ -61,7 +65,7 @@ struct htlc_out { /* If a local error, this is non-zero. */ enum onion_type failcode; - /* Either a remote error, or local error if !(failure & BADONION). */ + /* For a remote error. */ const u8 *failuremsg; /* If we fulfilled, here's the preimage. */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 514a7877b..7087c1da7 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -79,7 +79,8 @@ static bool htlc_out_update_state(struct peer *peer, static void fail_in_htlc(struct htlc_in *hin, enum onion_type failcode, - const u8 *failuremsg) + const u8 *failuremsg, + const struct short_channel_id *out_channelid) { assert(!hin->preimage); @@ -88,6 +89,12 @@ static void fail_in_htlc(struct htlc_in *hin, if (failuremsg) hin->failuremsg = tal_dup_arr(hin, u8, failuremsg, tal_len(failuremsg), 0); + /* We need this set, since we send it to channeld. */ + if (hin->failcode & UPDATE) + hin->failoutchannel = *out_channelid; + else + memset(&hin->failoutchannel, 0, sizeof(hin->failoutchannel)); + /* We update state now to signal it's in progress, for persistence. */ htlc_in_update_state(hin->key.peer, hin, SENT_REMOVE_HTLC); @@ -95,93 +102,22 @@ static void fail_in_htlc(struct htlc_in *hin, if (!hin->key.peer->owner) return; - if (!hin->failuremsg) { - subd_send_msg(hin->key.peer->owner, - take(towire_channel_fail_htlc(hin, - hin->key.id, - NULL, - hin->failcode))); - } else { - subd_send_msg(hin->key.peer->owner, - take(towire_channel_fail_htlc(hin, hin->key.id, - hin->failuremsg, - 0))); - } -} - -static u8 *make_failmsg(const tal_t *ctx, - struct log *log, - u64 msatoshi, - enum onion_type failcode, - const u8 *channel_update) -{ - switch (failcode) { - case WIRE_INVALID_REALM: - return towire_invalid_realm(ctx); - case WIRE_TEMPORARY_NODE_FAILURE: - return towire_temporary_node_failure(ctx); - case WIRE_PERMANENT_NODE_FAILURE: - return towire_permanent_node_failure(ctx); - case WIRE_REQUIRED_NODE_FEATURE_MISSING: - return towire_required_node_feature_missing(ctx); - case WIRE_TEMPORARY_CHANNEL_FAILURE: - return towire_temporary_channel_failure(ctx, channel_update); - case WIRE_CHANNEL_DISABLED: - return towire_channel_disabled(ctx); - case WIRE_PERMANENT_CHANNEL_FAILURE: - return towire_permanent_channel_failure(ctx); - case WIRE_REQUIRED_CHANNEL_FEATURE_MISSING: - return towire_required_channel_feature_missing(ctx); - case WIRE_UNKNOWN_NEXT_PEER: - return towire_unknown_next_peer(ctx); - case WIRE_AMOUNT_BELOW_MINIMUM: - return towire_amount_below_minimum(ctx, msatoshi, channel_update); - case WIRE_FEE_INSUFFICIENT: - return towire_fee_insufficient(ctx, msatoshi, channel_update); - case WIRE_INCORRECT_CLTV_EXPIRY: - /* FIXME: cltv! */ - return towire_incorrect_cltv_expiry(ctx, 0, channel_update); - case WIRE_EXPIRY_TOO_SOON: - return towire_expiry_too_soon(ctx, channel_update); - case WIRE_EXPIRY_TOO_FAR: - return towire_expiry_too_far(ctx); - case WIRE_UNKNOWN_PAYMENT_HASH: - return towire_unknown_payment_hash(ctx); - case WIRE_INCORRECT_PAYMENT_AMOUNT: - return towire_incorrect_payment_amount(ctx); - case WIRE_FINAL_EXPIRY_TOO_SOON: - return towire_final_expiry_too_soon(ctx); - case WIRE_FINAL_INCORRECT_CLTV_EXPIRY: - /* FIXME: cltv! */ - return towire_final_incorrect_cltv_expiry(ctx, 0); - case WIRE_FINAL_INCORRECT_HTLC_AMOUNT: - return towire_final_incorrect_htlc_amount(ctx, msatoshi); - case WIRE_INVALID_ONION_VERSION: - case WIRE_INVALID_ONION_HMAC: - case WIRE_INVALID_ONION_KEY: - /* We don't have anything to add for these; code is enough */ - return NULL; - } - - log_broken(log, "Asked to create unknown failmsg %u:" - " using temp node failure instead", failcode); - return towire_temporary_node_failure(ctx); + subd_send_msg(hin->key.peer->owner, + take(towire_channel_fail_htlc(hin, + hin->key.id, + hin->failuremsg, + hin->failcode, + &hin->failoutchannel))); } /* 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) +static void local_fail_htlc(struct htlc_in *hin, enum onion_type failcode, + const struct short_channel_id *out_channel) { - u8 *msg; - log_info(hin->key.peer->log, "failed htlc %"PRIu64" code 0x%04x (%s)", hin->key.id, failcode, onion_type_name(failcode)); - /* FIXME: Get update! */ - msg = make_failmsg(hin, hin->key.peer->log, - hin->msatoshi, failcode, NULL); - - fail_in_htlc(hin, failcode, msg); - tal_free(msg); + fail_in_htlc(hin, failcode, NULL, out_channel); } /* localfail are for handing to the local payer if it's local. */ @@ -190,7 +126,8 @@ static void fail_out_htlc(struct htlc_out *hout, const char *localfail) htlc_out_check(hout, __func__); assert(hout->failcode || hout->failuremsg); if (hout->in) { - fail_in_htlc(hout->in, hout->failcode, hout->failuremsg); + fail_in_htlc(hout->in, hout->failcode, hout->failuremsg, + hout->key.peer->scid); } else { payment_failed(hout->key.peer->ld, hout, localfail); } @@ -364,7 +301,9 @@ static void handle_localpay(struct htlc_in *hin, return; fail: - local_fail_htlc(hin, failcode); + /* Final hop never sends an UPDATE. */ + assert(!(failcode & UPDATE)); + local_fail_htlc(hin, failcode, NULL); } /* @@ -378,10 +317,7 @@ static void hout_subd_died(struct htlc_out *hout) "Failing HTLC %"PRIu64" due to peer death", hout->key.id); - hout->failuremsg = make_failmsg(hout, hout->key.peer->log, - hout->msatoshi, - WIRE_TEMPORARY_CHANNEL_FAILURE, - NULL); + hout->failcode = WIRE_TEMPORARY_CHANNEL_FAILURE; fail_out_htlc(hout, "Outgoing subdaemon died"); } @@ -410,7 +346,8 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds, (const char *)failurestr); payment_failed(hout->key.peer->ld, hout, localfail); } else - local_fail_htlc(hout->in, failure_code); + local_fail_htlc(hout->in, failure_code, + hout->key.peer->scid); return; } @@ -480,9 +417,10 @@ static void forward_htlc(struct htlc_in *hin, struct lightningd *ld = hin->key.peer->ld; struct peer *next = peer_by_id(ld, next_hop); - if (!next) { - failcode = WIRE_UNKNOWN_NEXT_PEER; - goto fail; + /* Unknown peer, or peer not ready. */ + if (!next || !next->scid) { + local_fail_htlc(hin, WIRE_UNKNOWN_NEXT_PEER, NULL); + return; } /* BOLT #7: @@ -551,7 +489,7 @@ static void forward_htlc(struct htlc_in *hin, return; fail: - local_fail_htlc(hin, failcode); + local_fail_htlc(hin, failcode, next->scid); } /* Temporary information, while we resolve the next hop */ @@ -578,7 +516,7 @@ static void channel_resolve_reply(struct subd *gossip, const u8 *msg, } if (tal_count(nodes) == 0) { - local_fail_htlc(gr->hin, WIRE_UNKNOWN_NEXT_PEER); + local_fail_htlc(gr->hin, WIRE_UNKNOWN_NEXT_PEER, NULL); return; } else if (tal_count(nodes) != 2) { log_broken(gossip->log, @@ -826,12 +764,10 @@ void onchain_failed_our_htlc(const struct peer *peer, struct htlc_out *hout = find_htlc_out_by_ripemd(peer, &htlc->ripemd); /* Don't fail twice! */ - if (hout->failuremsg) + if (hout->failuremsg || hout->failcode) return; - hout->failuremsg = make_failmsg(hout, peer->log, hout->msatoshi, - WIRE_PERMANENT_CHANNEL_FAILURE, - NULL); + hout->failcode = WIRE_PERMANENT_CHANNEL_FAILURE; if (!hout->in) { char *localfail = tal_fmt(peer, "%s: %s", @@ -840,7 +776,8 @@ void onchain_failed_our_htlc(const struct peer *peer, payment_failed(hout->key.peer->ld, hout, localfail); tal_free(localfail); } else - local_fail_htlc(hout->in, WIRE_PERMANENT_CHANNEL_FAILURE); + local_fail_htlc(hout->in, WIRE_PERMANENT_CHANNEL_FAILURE, + hout->key.peer->scid); } static void remove_htlc_in(struct peer *peer, struct htlc_in *hin) @@ -1294,8 +1231,11 @@ void peer_got_revoke(struct peer *peer, const u8 *msg) if (!failcodes[i]) continue; + /* These are all errors before finding next hop. */ + assert(!(failcodes[i] & UPDATE)); + hin = find_htlc_in(&peer->ld->htlcs_in, peer, changed[i].id); - local_fail_htlc(hin, failcodes[i]); + local_fail_htlc(hin, failcodes[i], NULL); } wallet_channel_save(peer->ld->wallet, peer->channel); }