diff --git a/openingd/dualopend.c b/openingd/dualopend.c index d678d8106..0bb46b9b1 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -2358,6 +2358,186 @@ static void try_read_gossip_store(struct state *state) sync_crypto_write(state->pps, take(msg)); } +/* Try to handle a custommsg Returns true if it was a custom message and has + * been handled, false if the message was not handled. + */ +static bool dualopend_handle_custommsg(const u8 *msg) +{ +#if DEVELOPER + enum peer_wire type = fromwire_peektype(msg); + if (type % 2 == 1 && !peer_wire_is_defined(type)) { + /* The message is not part of the messages we know how to + * handle. Assuming this is a custommsg, we just forward it to the + * master. */ + wire_sync_write(REQ_FD, take(towire_custommsg_in(NULL, msg))); + return true; + } else { + return false; + } +#else + return false; +#endif +} + +/* BOLT #2: + * + * A receiving node: + * - if `option_static_remotekey` applies to the commitment transaction: + * - if `next_revocation_number` is greater than expected above, AND + * `your_last_per_commitment_secret` is correct for that + * `next_revocation_number` minus 1: + *... + * - otherwise, if it supports `option_data_loss_protect`: + * - if `next_revocation_number` is greater than expected above, + * AND `your_last_per_commitment_secret` is correct for that + * `next_revocation_number` minus 1: + */ +static void +check_future_dataloss_fields(struct state *state, + u64 next_revocation_number, + const struct secret *last_local_per_commit_secret) +{ + const u8 *msg; + bool correct; + + /* We're always at zero in dualopend */ + assert(next_revocation_number > 0); + + msg = towire_hsmd_check_future_secret(NULL, + next_revocation_number - 1, + last_local_per_commit_secret); + wire_sync_write(HSM_FD, take(msg)); + msg = wire_sync_read(tmpctx, HSM_FD); + + if (!fromwire_hsmd_check_future_secret_reply(msg, &correct)) + status_failed(STATUS_FAIL_HSM_IO, + "Bad hsm_check_future_secret_reply: %s", + tal_hex(tmpctx, msg)); + + if (!correct) + peer_failed(state->pps, + &state->channel_id, + "bad future last_local_per_commit_secret: %"PRIu64 + " vs %d", + next_revocation_number, 0); + + /* Oh shit, they really are from the future! */ + peer_billboard(true, "They have future commitment number %"PRIu64 + " vs our %d. We must wait for them to close!", + next_revocation_number, 0); + + + /* BOLT #2: + * - MUST NOT broadcast its commitment transaction. + * - SHOULD fail the channel. + */ + wire_sync_write(REQ_FD, + take(towire_dualopend_fail_fallen_behind(NULL))); + + /* We have to send them an error to trigger dropping to chain. */ + peer_failed(state->pps, &state->channel_id, + "Awaiting unilateral close"); +} + + +static void do_reconnect_dance(struct state *state) +{ + u8 *msg; + struct channel_id cid; + /* Note: BOLT #2 uses these names! */ + u64 next_commitment_number, next_revocation_number; + struct secret last_local_per_commit_secret, + last_remote_per_commit_secret; + struct pubkey remote_current_per_commit_point; + + /* BOLT #2: + * - if `next_revocation_number` equals 0: + * - MUST set `your_last_per_commitment_secret` to all zeroes + */ + /* We always have no revocations in dualopend */ + memset(&last_remote_per_commit_secret, 0, + sizeof(last_remote_per_commit_secret)); + + /* We always send reconnect/reestablish */ + msg = towire_channel_reestablish + (NULL, &state->channel_id, 1, 0, + &last_remote_per_commit_secret, + &state->first_per_commitment_point[LOCAL]); + sync_crypto_write(state->pps, take(msg)); + + peer_billboard(false, "Sent reestablish, waiting for theirs"); + bool soft_error = state->funding_locked[REMOTE] + || state->funding_locked[LOCAL]; + + /* Read until they say something interesting (don't forward + * gossip *to* them yet: we might try sending channel_update + * before we've reestablished channel). */ + do { + clean_tmpctx(); + msg = sync_crypto_read(tmpctx, state->pps); + } while (dualopend_handle_custommsg(msg) + || handle_peer_gossip_or_error(state->pps, + &state->channel_id, + soft_error, msg)); + + if (!fromwire_channel_reestablish + (msg, &cid, + &next_commitment_number, + &next_revocation_number, + &last_local_per_commit_secret, + &remote_current_per_commit_point)) + peer_failed(state->pps, + &state->channel_id, + "Bad reestablish msg: %s %s", + peer_wire_name(fromwire_peektype(msg)), + tal_hex(msg, msg)); + + check_channel_id(state, &cid, &state->channel_id); + + status_debug("Got dualopend reestablish commit=%"PRIu64 + " revoke=%"PRIu64, + next_commitment_number, + next_revocation_number); + + /* BOLT #2: + * - if it has not sent `revoke_and_ack`, AND + * `next_revocation_number` is not equal to 0: + * - SHOULD fail the channel. + */ + /* It's possible that we've opened an outdated copy of the + * database, and the peer is very much ahead of us. + */ + if (next_revocation_number != 0) { + /* Remote claims it's ahead of us: can it prove it? + * Does not return. */ + check_future_dataloss_fields(state, + next_revocation_number, + &last_local_per_commit_secret); + } + + if (next_commitment_number != 1) + peer_failed(state->pps, + &state->channel_id, + "bad reestablish commitment_number: %"PRIu64 + " vs %d", + next_commitment_number, 1); + + /* It's possible we sent our sigs, but they didn't get them. + * Resend our signatures, just in case */ + if (psbt_side_finalized(state->psbt, state->our_role) + && !state->funding_locked[REMOTE]) { + msg = psbt_to_tx_sigs_msg(NULL, state, state->psbt); + sync_crypto_write(state->pps, take(msg)); + } + + if (state->funding_locked[LOCAL]) { + status_debug("we've got locked, sending locked"); + send_funding_locked(state); + } + + peer_billboard(true, "Reconnected, and reestablished."); +} + /*~ Is this message of type `error` with the special zero-id * "fail-everything"? If lightningd asked us to send such a thing, we're * done. */ @@ -2664,6 +2844,12 @@ int main(int argc, char *argv[]) pollfd[2].fd = state->pps->peer_fd; pollfd[2].events = POLLIN; + /* Do reconnect, if need be */ + if (state->channel) { + do_reconnect_dance(state); + state->reconnected = true; + } + /* We exit when we get a conclusion to write to lightningd: either * opening_funder_reply or opening_fundee. */ msg = NULL;