gossipd: provide helper to get a channels cupdate, create routine to use it.

The idea is that gossipd can give us the cupdate we need for an error, and
we wire things up so that we ask for it (async) just before we send the
error to the subdaemon.

I tried many other things, but they were all too high-risk.

1. We need to ask gossipd every time, since it produces these lazily
   (in particular, it doesn't actually generate an offline update unless
   the channel is used).
2. We can't do async calls in random places, since we'll end up with
   an HTLC in limbo.  What if another path tries to fail it at the same time?
3. This allows us to use a temporary_node_failure error, and upgrade it
   when gossipd replies.  This doesn't change any existing assumptions.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2020-02-27 14:17:16 +10:30
parent c51c6f9133
commit 247d249ea8
6 changed files with 161 additions and 39 deletions

View File

@ -82,6 +82,14 @@ msgdata,gossip_get_channel_peer_reply,peer_id,?node_id,
msgdata,gossip_get_channel_peer_reply,stripped_update_len,u16,
msgdata,gossip_get_channel_peer_reply,stripped_update,u8,stripped_update_len
# Given a short_channel_id, return the latest (stripped) update for error msg.
msgtype,gossip_get_stripped_cupdate,3010
msgdata,gossip_get_stripped_cupdate,channel_id,short_channel_id,
msgtype,gossip_get_stripped_cupdate_reply,3110
msgdata,gossip_get_stripped_cupdate_reply,stripped_update_len,u16,
msgdata,gossip_get_stripped_cupdate_reply,stripped_update,u8,stripped_update_len
# gossipd->master: we're closing this channel.
msgtype,gossip_local_channel_close,3027
msgdata,gossip_local_channel_close,short_channel_id,short_channel_id,

Can't render this file because it has a wrong number of fields in line 6.

View File

@ -1377,6 +1377,46 @@ static struct io_plan *get_channel_peer(struct io_conn *conn,
return daemon_conn_read_next(conn, daemon->master);
}
/*~ lightningd: so, get me the latest update for this local channel,
* so I can include it in an error message. */
static struct io_plan *get_stripped_cupdate(struct io_conn *conn,
struct daemon *daemon, const u8 *msg)
{
struct short_channel_id scid;
struct local_chan *local_chan;
const u8 *stripped_update;
if (!fromwire_gossip_get_stripped_cupdate(msg, &scid))
master_badmsg(WIRE_GOSSIP_GET_STRIPPED_CUPDATE, msg);
local_chan = local_chan_map_get(&daemon->rstate->local_chan_map, &scid);
if (!local_chan) {
status_debug("Failed to resolve local channel %s",
type_to_string(tmpctx, struct short_channel_id, &scid));
stripped_update = NULL;
} else {
const struct half_chan *hc;
/* Since we're going to use it, make sure it's up-to-date. */
refresh_local_channel(daemon, local_chan, false);
hc = &local_chan->chan->half[local_chan->direction];
if (is_halfchan_defined(hc)) {
const u8 *update;
update = gossip_store_get(tmpctx, daemon->rstate->gs,
hc->bcast.index);
stripped_update = tal_dup_arr(tmpctx, u8, update + 2,
tal_count(update) - 2, 0);
} else
stripped_update = NULL;
}
daemon_conn_send(daemon->master,
take(towire_gossip_get_stripped_cupdate_reply(NULL,
stripped_update)));
return daemon_conn_read_next(conn, daemon->master);
}
/*~ We queue incoming channel_announcement pending confirmation from lightningd
* that it really is an unspent output. Here's its reply. */
static struct io_plan *handle_txout_reply(struct io_conn *conn,
@ -1574,6 +1614,9 @@ static struct io_plan *recv_req(struct io_conn *conn,
case WIRE_GOSSIP_GET_CHANNEL_PEER:
return get_channel_peer(conn, daemon, msg);
case WIRE_GOSSIP_GET_STRIPPED_CUPDATE:
return get_stripped_cupdate(conn, daemon, msg);
case WIRE_GOSSIP_GET_TXOUT_REPLY:
return handle_txout_reply(conn, daemon, msg);
@ -1621,6 +1664,7 @@ static struct io_plan *recv_req(struct io_conn *conn,
case WIRE_GOSSIP_GETCHANNELS_REPLY:
case WIRE_GOSSIP_PING_REPLY:
case WIRE_GOSSIP_GET_CHANNEL_PEER_REPLY:
case WIRE_GOSSIP_GET_STRIPPED_CUPDATE_REPLY:
case WIRE_GOSSIP_GET_INCOMING_CHANNELS_REPLY:
case WIRE_GOSSIP_GET_TXOUT:
case WIRE_GOSSIP_DEV_MEMLEAK_REPLY:

View File

@ -139,6 +139,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds)
case WIRE_GOSSIP_GETCHANNELS_REQUEST:
case WIRE_GOSSIP_PING:
case WIRE_GOSSIP_GET_CHANNEL_PEER:
case WIRE_GOSSIP_GET_STRIPPED_CUPDATE:
case WIRE_GOSSIP_GET_TXOUT_REPLY:
case WIRE_GOSSIP_OUTPOINT_SPENT:
case WIRE_GOSSIP_PAYMENT_FAILURE:
@ -158,6 +159,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds)
case WIRE_GOSSIP_GET_INCOMING_CHANNELS_REPLY:
case WIRE_GOSSIP_DEV_MEMLEAK_REPLY:
case WIRE_GOSSIP_DEV_COMPACT_STORE_REPLY:
case WIRE_GOSSIP_GET_STRIPPED_CUPDATE_REPLY:
break;
case WIRE_GOSSIP_PING_REPLY:

View File

@ -128,6 +128,74 @@ static struct failed_htlc *mk_failed_htlc(const tal_t *ctx,
return f;
}
static void tell_channeld_htlc_failed(const struct htlc_in *hin,
const struct failed_htlc *failed_htlc)
{
/* Tell peer, if we can. */
if (!hin->key.channel->owner)
return;
/* onchaind doesn't care, it can't do anything but wait */
if (channel_on_chain(hin->key.channel))
return;
subd_send_msg(hin->key.channel->owner,
take(towire_channel_fail_htlc(NULL, failed_htlc)));
}
struct failmsg_update_cbdata {
struct htlc_in *hin;
const u8 *failmsg_needs_update;
};
static void failmsg_update_reply(struct subd *gossipd,
const u8 *msg,
const int *unused,
struct failmsg_update_cbdata *cbdata)
{
u8 *failmsg;
u8 *stripped_update;
struct failed_htlc *failed_htlc;
/* In theory, we could have no update because channel suddenly closed,
* but it's v. unlikely */
if (!fromwire_gossip_get_stripped_cupdate_reply(msg, msg,
&stripped_update)
|| !tal_count(stripped_update)) {
log_broken(gossipd->log,
"bad fromwire_gossip_get_stripped_cupdate %s",
tal_hex(msg, msg));
failmsg = towire_temporary_node_failure(NULL);
} else {
/* End of failmsg is two zero bytes (empty update). */
assert(tal_count(cbdata->failmsg_needs_update) >= 2);
failmsg = tal_dup_arr(msg, u8,
cbdata->failmsg_needs_update,
tal_count(cbdata->failmsg_needs_update)-2,
0);
towire_u16(&failmsg, tal_count(stripped_update));
towire_u8_array(&failmsg,
stripped_update, tal_count(stripped_update));
}
/* Now we replace dummy failonion with this real one */
tal_free(cbdata->hin->failonion);
cbdata->hin->failonion
= create_onionreply(cbdata->hin,
cbdata->hin->shared_secret,
failmsg);
wallet_htlc_update(gossipd->ld->wallet,
cbdata->hin->dbid, cbdata->hin->hstate,
cbdata->hin->preimage,
cbdata->hin->badonion,
cbdata->hin->failonion, NULL);
failed_htlc = mk_failed_htlc(tmpctx,
cbdata->hin, cbdata->hin->failonion);
tell_channeld_htlc_failed(cbdata->hin, failed_htlc);
}
static void fail_in_htlc(struct htlc_in *hin,
const struct onionreply *failonion TAKES)
{
@ -140,17 +208,8 @@ static void fail_in_htlc(struct htlc_in *hin,
htlc_in_update_state(hin->key.channel, hin, SENT_REMOVE_HTLC);
htlc_in_check(hin, __func__);
/* Tell peer, if we can. */
if (!hin->key.channel->owner)
return;
/* onchaind doesn't care, it can't do anything but wait */
if (channel_on_chain(hin->key.channel))
return;
failed_htlc = mk_failed_htlc(tmpctx, hin, hin->failonion);
subd_send_msg(hin->key.channel->owner,
take(towire_channel_fail_htlc(NULL, failed_htlc)));
tell_channeld_htlc_failed(hin, failed_htlc);
}
/* Immediately fail HTLC with a BADONION code */
@ -167,17 +226,7 @@ static void local_fail_in_htlc_badonion(struct htlc_in *hin,
htlc_in_check(hin, __func__);
failed_htlc = mk_failed_htlc_badonion(tmpctx, hin, badonion);
/* Tell peer, if we can. */
if (!hin->key.channel->owner)
return;
/* onchaind doesn't care, it can't do anything but wait */
if (channel_on_chain(hin->key.channel))
return;
subd_send_msg(hin->key.channel->owner,
take(towire_channel_fail_htlc(NULL, failed_htlc)));
tell_channeld_htlc_failed(hin, failed_htlc);
}
/* This is used for cases where we can immediately fail the HTLC. */
@ -193,6 +242,34 @@ void local_fail_in_htlc(struct htlc_in *hin, const u8 *failmsg TAKES)
fail_in_htlc(hin, take(failonion));
}
/* This is used for cases where we can immediately fail the HTLC, but
* need to append a channel_update. */
void local_fail_in_htlc_needs_update(struct htlc_in *hin,
const u8 *failmsg_needs_update TAKES,
const struct short_channel_id *failmsg_scid)
{
struct failmsg_update_cbdata *cbdata;
/* To avoid the state where we have no failonion, we use a temporary
* one, and update once we get the reply from gossipd. */
assert(!hin->preimage);
hin->failonion = create_onionreply(hin,
hin->shared_secret,
towire_temporary_node_failure(tmpctx));
/* We update state now to signal it's in progress, for persistence. */
htlc_in_update_state(hin->key.channel, hin, SENT_REMOVE_HTLC);
htlc_in_check(hin, __func__);
cbdata = tal(hin, struct failmsg_update_cbdata);
cbdata->hin = hin;
cbdata->failmsg_needs_update
= tal_dup_talarr(cbdata, u8, failmsg_needs_update);
subd_req(cbdata, hin->key.channel->peer->ld->gossip,
take(towire_gossip_get_stripped_cupdate(NULL, failmsg_scid)),
-1, 0, failmsg_update_reply, cbdata);
}
/* Helper to create (common) WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS */
const u8 *failmsg_incorrect_or_unknown(const tal_t *ctx,
const struct htlc_in *hin)

View File

@ -74,6 +74,9 @@ void htlcs_resubmit(struct lightningd *ld,
/* For HTLCs which terminate here, invoice payment calls one of these. */
void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage);
void local_fail_in_htlc(struct htlc_in *hin, const u8 *failmsg TAKES);
void local_fail_in_htlc_needs_update(struct htlc_in *hin,
const u8 *failmsg_needs_update TAKES,
const struct short_channel_id *failmsg_scid);
/* This json process will be used as the serialize method for
* forward_event_notification_gen and be used in

View File

@ -123,6 +123,9 @@ bool fromwire_custommsg_in(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8
/* Generated stub for fromwire_gossip_get_channel_peer_reply */
bool fromwire_gossip_get_channel_peer_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id **peer_id UNNEEDED, u8 **stripped_update UNNEEDED)
{ fprintf(stderr, "fromwire_gossip_get_channel_peer_reply called!\n"); abort(); }
/* Generated stub for fromwire_gossip_get_stripped_cupdate_reply */
bool fromwire_gossip_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **stripped_update UNNEEDED)
{ fprintf(stderr, "fromwire_gossip_get_stripped_cupdate_reply called!\n"); abort(); }
/* Generated stub for fromwire_hsm_ecdh_resp */
bool fromwire_hsm_ecdh_resp(const void *p UNNEEDED, struct secret *ss UNNEEDED)
{ fprintf(stderr, "fromwire_hsm_ecdh_resp called!\n"); abort(); }
@ -577,9 +580,6 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED,
void *arg) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "topology_add_sync_waiter_ called!\n"); abort(); }
/* Generated stub for towire_amount_below_minimum */
u8 *towire_amount_below_minimum(const tal_t *ctx UNNEEDED, struct amount_msat htlc_msat UNNEEDED, const u8 *channel_update UNNEEDED)
{ fprintf(stderr, "towire_amount_below_minimum called!\n"); abort(); }
/* Generated stub for towire_channel_dev_memleak */
u8 *towire_channel_dev_memleak(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_channel_dev_memleak called!\n"); abort(); }
@ -645,6 +645,9 @@ u8 *towire_final_incorrect_htlc_amount(const tal_t *ctx UNNEEDED, struct amount_
/* Generated stub for towire_gossip_get_channel_peer */
u8 *towire_gossip_get_channel_peer(const tal_t *ctx UNNEEDED, const struct short_channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_gossip_get_channel_peer called!\n"); abort(); }
/* Generated stub for towire_gossip_get_stripped_cupdate */
u8 *towire_gossip_get_stripped_cupdate(const tal_t *ctx UNNEEDED, const struct short_channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_gossip_get_stripped_cupdate called!\n"); abort(); }
/* Generated stub for towire_hsm_ecdh_req */
u8 *towire_hsm_ecdh_req(const tal_t *ctx UNNEEDED, const struct pubkey *point UNNEEDED)
{ fprintf(stderr, "towire_hsm_ecdh_req called!\n"); abort(); }
@ -657,24 +660,9 @@ u8 *towire_incorrect_cltv_expiry(const tal_t *ctx UNNEEDED, u32 cltv_expiry UNNE
/* Generated stub for towire_incorrect_or_unknown_payment_details */
u8 *towire_incorrect_or_unknown_payment_details(const tal_t *ctx UNNEEDED, struct amount_msat htlc_msat UNNEEDED, u32 height UNNEEDED)
{ fprintf(stderr, "towire_incorrect_or_unknown_payment_details called!\n"); abort(); }
/* Generated stub for towire_invalid_onion_hmac */
u8 *towire_invalid_onion_hmac(const tal_t *ctx UNNEEDED, const struct sha256 *sha256_of_onion UNNEEDED)
{ fprintf(stderr, "towire_invalid_onion_hmac called!\n"); abort(); }
/* Generated stub for towire_invalid_onion_key */
u8 *towire_invalid_onion_key(const tal_t *ctx UNNEEDED, const struct sha256 *sha256_of_onion UNNEEDED)
{ fprintf(stderr, "towire_invalid_onion_key called!\n"); abort(); }
/* Generated stub for towire_invalid_onion_payload */
u8 *towire_invalid_onion_payload(const tal_t *ctx UNNEEDED, varint type UNNEEDED, u16 offset UNNEEDED)
{ fprintf(stderr, "towire_invalid_onion_payload called!\n"); abort(); }
/* Generated stub for towire_invalid_onion_version */
u8 *towire_invalid_onion_version(const tal_t *ctx UNNEEDED, const struct sha256 *sha256_of_onion UNNEEDED)
{ fprintf(stderr, "towire_invalid_onion_version called!\n"); abort(); }
/* Generated stub for towire_invalid_realm */
u8 *towire_invalid_realm(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_invalid_realm called!\n"); abort(); }
/* Generated stub for towire_mpp_timeout */
u8 *towire_mpp_timeout(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_mpp_timeout called!\n"); abort(); }
/* Generated stub for towire_onchain_dev_memleak */
u8 *towire_onchain_dev_memleak(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_onchain_dev_memleak called!\n"); abort(); }