diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 4f50d4b37..1882cdd06 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -1,8 +1,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -151,6 +153,46 @@ void channel_config(struct lightningd *ld, ours->channel_reserve = AMOUNT_SAT(UINT64_MAX); } +void handle_reestablish(struct lightningd *ld, + const struct node_id *peer_id, + const struct channel_id *channel_id, + const u8 *reestablish, + struct per_peer_state *pps) +{ + struct peer *peer; + struct channel *c; + + /* We very carefully re-xmit the last reestablish, so they can get + * their secrets back. We don't otherwise touch them. */ + peer = peer_by_id(ld, peer_id); + if (peer) + c = find_channel_by_id(peer, channel_id); + else + c = NULL; + + if (c && channel_closed(c)) { + log_debug(c->log, "Reestablish on %s channel: using channeld to reply", + channel_state_name(c)); + peer_start_channeld(c, pps, NULL, true, reestablish); + } else { + const u8 *err = towire_errorfmt(tmpctx, channel_id, + "Unknown channel for reestablish"); + log_debug(ld->log, "Reestablish on UNKNOWN channel %s", + type_to_string(tmpctx, struct channel_id, channel_id)); + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, peer_id, + pps, err))); + subd_send_fd(ld->connectd, pps->peer_fd); + subd_send_fd(ld->connectd, pps->gossip_fd); + subd_send_fd(ld->connectd, pps->gossip_store_fd); + /* Don't close those fds! */ + pps->peer_fd + = pps->gossip_fd + = pps->gossip_store_fd + = -1; + } +} + #if DEVELOPER /* Indented to avoid include ordering check */ #include diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index 212a9109c..b499407e6 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -118,6 +118,12 @@ void channel_config(struct lightningd *ld, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity); +void handle_reestablish(struct lightningd *ld, + const struct node_id *peer_id, + const struct channel_id *channel_id, + const u8 *reestablish, + struct per_peer_state *pps); + #if DEVELOPER struct command; /* Calls report_leak_info() async. */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 5e740e34a..ef23e6480 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -801,6 +801,31 @@ static void opening_got_offer(struct subd *openingd, plugin_hook_call_openchannel(openingd->ld, payload); } +static void opening_got_reestablish(struct subd *openingd, const u8 *msg, + const int fds[3], + struct uncommitted_channel *uc) +{ + struct lightningd *ld = openingd->ld; + struct node_id peer_id = uc->peer->id; + struct channel_id channel_id; + u8 *reestablish; + struct per_peer_state *pps; + + if (!fromwire_openingd_got_reestablish(tmpctx, msg, &channel_id, + &reestablish, &pps)) { + log_broken(openingd->log, "Malformed opening_got_reestablish %s", + tal_hex(tmpctx, msg)); + tal_free(openingd); + return; + } + per_peer_state_set_fds_arr(pps, fds); + + /* This could free peer */ + tal_free(uc); + + handle_reestablish(ld, &peer_id, &channel_id, reestablish, pps); +} + static unsigned int openingd_msg(struct subd *openingd, const u8 *msg, const int *fds) { @@ -848,6 +873,12 @@ static unsigned int openingd_msg(struct subd *openingd, opening_got_offer(openingd, msg, uc); return 0; + case WIRE_OPENINGD_GOT_REESTABLISH: + if (tal_count(fds) != 3) + return 3; + opening_got_reestablish(openingd, msg, fds, uc); + return 0; + /* We send these! */ case WIRE_OPENINGD_INIT: case WIRE_OPENINGD_FUNDER_START: diff --git a/openingd/openingd.c b/openingd/openingd.c index 03f34059f..051b153ea 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1149,6 +1149,7 @@ static u8 *handle_peer_in(struct state *state) u8 *msg = sync_crypto_read(tmpctx, state->pps); enum peer_wire t = fromwire_peektype(msg); struct channel_id channel_id; + bool extracted; if (t == WIRE_OPEN_CHANNEL) return fundee_channel(state, msg); @@ -1170,21 +1171,20 @@ static u8 *handle_peer_in(struct state *state) &state->channel_id, false, msg)) return NULL; - if (extract_channel_id(msg, &channel_id)) { - sync_crypto_write(state->pps, - take(towire_errorfmt(NULL, - &channel_id, - "Unexpected message %s: %s", - peer_wire_name(t), - tal_hex(tmpctx, msg)))); - } else { - sync_crypto_write(state->pps, - take(towire_warningfmt(NULL, - NULL, - "Unexpected message %s: %s", - peer_wire_name(t), - tal_hex(tmpctx, msg)))); + extracted = extract_channel_id(msg, &channel_id); + + /* Reestablish on some now-closed channel? Be nice. */ + if (extracted && fromwire_peektype(msg) == WIRE_CHANNEL_REESTABLISH) { + return towire_openingd_got_reestablish(NULL, + &channel_id, msg, + state->pps); } + sync_crypto_write(state->pps, + take(towire_warningfmt(NULL, + extracted ? &channel_id : NULL, + "Unexpected message %s: %s", + peer_wire_name(t), + tal_hex(tmpctx, msg)))); /* FIXME: We don't actually want master to try to send an * error, since peer is transient. This is a hack. @@ -1296,6 +1296,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_OPENINGD_FUNDER_FAILED: case WIRE_OPENINGD_GOT_OFFER: case WIRE_OPENINGD_GOT_OFFER_REPLY: + case WIRE_OPENINGD_GOT_REESTABLISH: break; } diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index 0c4e6ff80..84f51bccf 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -29,6 +29,13 @@ msgdata,openingd_init,option_anchor_outputs,bool, msgdata,openingd_init,dev_temporary_channel_id,?byte,32 msgdata,openingd_init,dev_fast_gossip,bool, +# Openingd->master: they tried to reestablish a channel. +msgtype,openingd_got_reestablish,6001 +msgdata,openingd_got_reestablish,channel_id,channel_id, +msgdata,openingd_got_reestablish,len,u16, +msgdata,openingd_got_reestablish,msg,u8,len +msgdata,openingd_got_reestablish,pps,per_peer_state, + # Openingd->master: they offered channel, should we continue? msgtype,openingd_got_offer,6005 msgdata,openingd_got_offer,funding_satoshis,amount_sat, diff --git a/openingd/openingd_wiregen.c b/openingd/openingd_wiregen.c index 7f615aa09..76bc14a30 100644 --- a/openingd/openingd_wiregen.c +++ b/openingd/openingd_wiregen.c @@ -21,6 +21,7 @@ const char *openingd_wire_name(int e) switch ((enum openingd_wire)e) { case WIRE_OPENINGD_INIT: return "WIRE_OPENINGD_INIT"; + case WIRE_OPENINGD_GOT_REESTABLISH: return "WIRE_OPENINGD_GOT_REESTABLISH"; case WIRE_OPENINGD_GOT_OFFER: return "WIRE_OPENINGD_GOT_OFFER"; case WIRE_OPENINGD_GOT_OFFER_REPLY: return "WIRE_OPENINGD_GOT_OFFER_REPLY"; case WIRE_OPENINGD_FUNDER_REPLY: return "WIRE_OPENINGD_FUNDER_REPLY"; @@ -42,6 +43,7 @@ bool openingd_wire_is_defined(u16 type) { switch ((enum openingd_wire)type) { case WIRE_OPENINGD_INIT:; + case WIRE_OPENINGD_GOT_REESTABLISH:; case WIRE_OPENINGD_GOT_OFFER:; case WIRE_OPENINGD_GOT_OFFER_REPLY:; case WIRE_OPENINGD_FUNDER_REPLY:; @@ -138,6 +140,39 @@ bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainp return cursor != NULL; } +/* WIRE: OPENINGD_GOT_REESTABLISH */ +/* Openingd->master: they tried to reestablish a channel. */ +u8 *towire_openingd_got_reestablish(const tal_t *ctx, const struct channel_id *channel_id, const u8 *msg, const struct per_peer_state *pps) +{ + u16 len = tal_count(msg); + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_OPENINGD_GOT_REESTABLISH); + towire_channel_id(&p, channel_id); + towire_u16(&p, len); + towire_u8_array(&p, msg, len); + towire_per_peer_state(&p, pps); + + return memcheck(p, tal_count(p)); +} +bool fromwire_openingd_got_reestablish(const tal_t *ctx, const void *p, struct channel_id *channel_id, u8 **msg, struct per_peer_state **pps) +{ + u16 len; + + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_OPENINGD_GOT_REESTABLISH) + return false; + fromwire_channel_id(&cursor, &plen, channel_id); + len = fromwire_u16(&cursor, &plen); + // 2nd case msg + *msg = len ? tal_arr(ctx, u8, len) : NULL; + fromwire_u8_array(&cursor, &plen, *msg, len); + *pps = fromwire_per_peer_state(ctx, &cursor, &plen); + return cursor != NULL; +} + /* WIRE: OPENINGD_GOT_OFFER */ /* Openingd->master: they offered channel */ u8 *towire_openingd_got_offer(const tal_t *ctx, struct amount_sat funding_satoshis, struct amount_msat push_msat, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_sat channel_reserve_satoshis, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, const u8 *shutdown_scriptpubkey) @@ -569,4 +604,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:d2fcabdf157b098608e47dcdc37db0f46fe8d466d74159969544d7c4bb77f061 +// SHA256STAMP:005577de0219577522210df32b3ed325f028b24fc25d3b77a1dc770077381b6b diff --git a/openingd/openingd_wiregen.h b/openingd/openingd_wiregen.h index 047c82110..1eaa101bb 100644 --- a/openingd/openingd_wiregen.h +++ b/openingd/openingd_wiregen.h @@ -18,6 +18,8 @@ enum openingd_wire { WIRE_OPENINGD_INIT = 6000, + /* Openingd->master: they tried to reestablish a channel. */ + WIRE_OPENINGD_GOT_REESTABLISH = 6001, /* Openingd->master: they offered channel */ WIRE_OPENINGD_GOT_OFFER = 6005, /* master->openingd: optional rejection message */ @@ -61,6 +63,11 @@ bool openingd_wire_is_defined(u16 type); u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, u32 min_feerate, u32 max_feerate, const u8 *lfeatures, bool option_static_remotekey, bool option_anchor_outputs, const struct channel_id *dev_temporary_channel_id, bool dev_fast_gossip); bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u32 *min_feerate, u32 *max_feerate, u8 **lfeatures, bool *option_static_remotekey, bool *option_anchor_outputs, struct channel_id **dev_temporary_channel_id, bool *dev_fast_gossip); +/* WIRE: OPENINGD_GOT_REESTABLISH */ +/* Openingd->master: they tried to reestablish a channel. */ +u8 *towire_openingd_got_reestablish(const tal_t *ctx, const struct channel_id *channel_id, const u8 *msg, const struct per_peer_state *pps); +bool fromwire_openingd_got_reestablish(const tal_t *ctx, const void *p, struct channel_id *channel_id, u8 **msg, struct per_peer_state **pps); + /* WIRE: OPENINGD_GOT_OFFER */ /* Openingd->master: they offered channel */ u8 *towire_openingd_got_offer(const tal_t *ctx, struct amount_sat funding_satoshis, struct amount_msat push_msat, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_sat channel_reserve_satoshis, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, const u8 *shutdown_scriptpubkey); @@ -121,4 +128,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_OPENINGD_OPENINGD_WIREGEN_H */ -// SHA256STAMP:d2fcabdf157b098608e47dcdc37db0f46fe8d466d74159969544d7c4bb77f061 +// SHA256STAMP:005577de0219577522210df32b3ed325f028b24fc25d3b77a1dc770077381b6b diff --git a/tests/test_connection.py b/tests/test_connection.py index 09b6c429f..9db21ac9f 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1149,7 +1149,7 @@ def test_funding_by_utxos(node_factory, bitcoind): @pytest.mark.developer("needs dev_forget_channel") @pytest.mark.openchannel('v1') def test_funding_external_wallet_corners(node_factory, bitcoind): - l1 = node_factory.get_node(may_reconnect=True, allow_broken_log=True) + l1 = node_factory.get_node(may_reconnect=True) l2 = node_factory.get_node(may_reconnect=True) amount = 2**24 @@ -1242,7 +1242,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): # on reconnect, channel should get destroyed l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - l1.daemon.wait_for_log('Unexpected message WIRE_CHANNEL_REESTABLISH') + l1.daemon.wait_for_log('Reestablish on UNKNOWN channel') wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 0) wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0)