Skip to content

Commit

Permalink
closingd: retransmit 'funding_locked' if we reconnect without any update
Browse files Browse the repository at this point in the history
As per BOLT02 #message-retransmission :
if `next_commitment_number` is 1 in both the `channel_reestablish` it sent and received:
    - MUST retransmit `funding_locked`
  • Loading branch information
darosior authored and rustyrussell committed Sep 10, 2019
1 parent 57a8951 commit a7cbe93
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- bolt11: support for parsing feature bits (field `9`).

- Protocol: we now retransmit `funding_locked` upon reconnection while closing if there was no update
### Changed

- JSON API: `txprepare` now uses `outputs` as parameter other than `destination` and `satoshi`
Expand Down
2 changes: 2 additions & 0 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -2315,6 +2315,8 @@ static void peer_reconnect(struct peer *peer,
&& next_commitment_number == 1) {
u8 *msg;

status_debug("Retransmitting funding_locked for channel %s",
type_to_string(tmpctx, struct channel_id, &peer->channel_id));
/* Contains per commit point #1, for first post-opening commit */
msg = towire_funding_locked(NULL,
&peer->channel_id,
Expand Down
56 changes: 39 additions & 17 deletions closingd/closingd.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,25 +108,17 @@ static u8 *closing_read_peer_msg(const tal_t *ctx,
}
}

static void do_reconnect(struct per_peer_state *pps,
const struct channel_id *channel_id,
const u64 next_index[NUM_SIDES],
u64 revocations_received,
const u8 *channel_reestablish,
const u8 *final_scriptpubkey,
const struct secret *last_remote_per_commit_secret)
static struct pubkey get_per_commitment_point(u64 commitment_number)
{
u8 *msg;
struct channel_id their_channel_id;
u64 next_local_commitment_number, next_remote_revocation_number;
struct pubkey my_current_per_commitment_point;
struct pubkey commitment_point;
struct secret *s;

/* Our current per-commitment point is the commitment point in the last
* received signed commitment; HSM gives us that and the previous
* secret (which we don't need). */
msg = towire_hsm_get_per_commitment_point(NULL,
next_index[LOCAL]-1);
commitment_number);
if (!wire_sync_write(HSM_FD, take(msg)))
status_failed(STATUS_FAIL_HSM_IO,
"Writing get_per_commitment_point to HSM: %s",
Expand All @@ -138,11 +130,29 @@ static void do_reconnect(struct per_peer_state *pps,
"Reading resp get_per_commitment_point reply: %s",
strerror(errno));
if (!fromwire_hsm_get_per_commitment_point_reply(tmpctx, msg,
&my_current_per_commitment_point,
&s))
&commitment_point,
&s))
status_failed(STATUS_FAIL_HSM_IO,
"Bad per_commitment_point reply %s",
tal_hex(tmpctx, msg));
"Bad per_commitment_point reply %s",
tal_hex(tmpctx, msg));

return commitment_point;
}

static void do_reconnect(struct per_peer_state *pps,
const struct channel_id *channel_id,
const u64 next_index[NUM_SIDES],
u64 revocations_received,
const u8 *channel_reestablish,
const u8 *final_scriptpubkey,
const struct secret *last_remote_per_commit_secret)
{
u8 *msg;
struct channel_id their_channel_id;
u64 next_local_commitment_number, next_remote_revocation_number;
struct pubkey my_current_per_commitment_point, next_commitment_point;

my_current_per_commitment_point = get_per_commitment_point(next_index[LOCAL]-1);

/* BOLT #2:
*
Expand Down Expand Up @@ -207,8 +217,20 @@ static void do_reconnect(struct per_peer_state *pps,
msg = towire_shutdown(NULL, channel_id, final_scriptpubkey);
sync_crypto_write(pps, take(msg));

/* FIXME: Spec says to re-xmit funding_locked here if we haven't
* done any updates. */
/* BOLT #2:
*
* A node:
*...
* - if `next_commitment_number` is 1 in both the `channel_reestablish` it sent and received:
* - MUST retransmit `funding_locked`.
*/
if (next_index[REMOTE] == 1 && next_index[LOCAL] == 1) {
status_debug("Retransmitting funding_locked for channel %s",
type_to_string(tmpctx, struct channel_id, channel_id));
next_commitment_point = get_per_commitment_point(next_index[LOCAL]);
msg = towire_funding_locked(NULL, channel_id, &next_commitment_point);
sync_crypto_write(pps, take(msg));
}
}

static void send_offer(struct per_peer_state *pps,
Expand Down
27 changes: 27 additions & 0 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,33 @@ def test_reconnect_gossiping(node_factory):
l2.daemon.wait_for_log('processing now old peer gone')


@unittest.skipIf(not DEVELOPER, "needs dev-disconnect")
def test_reconnect_no_update(node_factory, executor):
"""
This tests if the `funding_locked` is sent if we receive a
`channel_reestablish` message with `next_commitment_number` == 1 and
our `next_commitment_number` == 1.
"""
disconnects = ["@WIRE_FUNDING_LOCKED", "@WIRE_SHUTDOWN"]
# Allow bad gossip because it might receive WIRE_CHANNEL_UPDATE before
# announcement before of the disconnection
l1 = node_factory.get_node(may_reconnect=True, allow_bad_gossip=True)
l2 = node_factory.get_node(disconnect=disconnects, may_reconnect=True)

# For channeld reconnection
l1.rpc.connect(l2.info["id"], "localhost", l2.port)
fundchannel_exec = executor.submit(l1.fund_channel, l2, 10**6, False)
l1.daemon.wait_for_log(r"channeld.* Retransmitting funding_locked for channel")
l1.stop()

# For closingd reconnection
scid = fundchannel_exec.result()
l1.daemon.start()
executor.submit(l1.rpc.close, scid, 0)
l2.daemon.wait_for_log(r"closingd.* Retransmitting funding_locked for channel")
l1.daemon.wait_for_log(r"CLOSINGD_COMPLETE")


def test_connect_stresstest(node_factory, executor):
# This test is unreliable, but it's better than nothing.
l1 = node_factory.get_node(may_reconnect=True)
Expand Down

0 comments on commit a7cbe93

Please sign in to comment.