|
| 1 | +/* Master-side control for the simpleclosed subdaemon (option_simple_close). */ |
| 2 | +#include "config.h" |
| 3 | +#include <bitcoin/script.h> |
| 4 | +#include <bitcoin/signature.h> |
| 5 | +#include <ccan/tal/str/str.h> |
| 6 | +#include <closingd/simpleclosed_wiregen.h> |
| 7 | +#include <common/fee_states.h> |
| 8 | +#include <common/shutdown_scriptpubkey.h> |
| 9 | +#include <errno.h> |
| 10 | +#include <hsmd/permissions.h> |
| 11 | +#include <inttypes.h> |
| 12 | +#include <lightningd/chaintopology.h> |
| 13 | +#include <lightningd/channel.h> |
| 14 | +#include <lightningd/channel_control.h> |
| 15 | +#include <lightningd/closing_control.h> |
| 16 | +#include <lightningd/connect_control.h> |
| 17 | +#include <lightningd/feerate.h> |
| 18 | +#include <lightningd/hsm_control.h> |
| 19 | +#include <lightningd/lightningd.h> |
| 20 | +#include <lightningd/peer_control.h> |
| 21 | +#include <lightningd/peer_fd.h> |
| 22 | +#include <lightningd/simple_close_control.h> |
| 23 | +#include <lightningd/subd.h> |
| 24 | +#include <wallet/wallet.h> |
| 25 | +#include <wally_bip32.h> |
| 26 | + |
| 27 | + |
| 28 | +/* Check that tx spends exactly our funding outpoint and every output goes |
| 29 | + * to a known shutdown script. Returns an error string, or NULL on success. */ |
| 30 | +static const char *close_tx_check(const tal_t *ctx, |
| 31 | + const struct channel *channel, |
| 32 | + const struct bitcoin_tx *tx) |
| 33 | +{ |
| 34 | + if (tx->wtx->num_inputs != 1) |
| 35 | + return tal_fmt(ctx, "expected 1 input, got %zu", |
| 36 | + tx->wtx->num_inputs); |
| 37 | + |
| 38 | + if (!wally_tx_input_spends(&tx->wtx->inputs[0], &channel->funding)) |
| 39 | + return tal_fmt(ctx, "does not spend funding outpoint %s", |
| 40 | + fmt_bitcoin_outpoint(ctx, &channel->funding)); |
| 41 | + |
| 42 | + for (size_t i = 0; i < tx->wtx->num_outputs; i++) { |
| 43 | + const struct wally_tx_output *out = &tx->wtx->outputs[i]; |
| 44 | + /* Elements has an explicit fee output with no script. */ |
| 45 | + if (out->script_len == 0) { |
| 46 | + if (chainparams->is_elements) |
| 47 | + continue; |
| 48 | + return tal_fmt(ctx, "output %zu has no script", i); |
| 49 | + } |
| 50 | + const u8 *script = tal_dup_arr(ctx, u8, |
| 51 | + out->script, out->script_len, 0); |
| 52 | + if (!scripteq(script, channel->shutdown_scriptpubkey[LOCAL]) |
| 53 | + && !scripteq(script, channel->shutdown_scriptpubkey[REMOTE])) |
| 54 | + return tal_fmt(ctx, |
| 55 | + "output %zu goes to unknown script %s", |
| 56 | + i, tal_hex(ctx, script)); |
| 57 | + } |
| 58 | + return NULL; |
| 59 | +} |
| 60 | + |
| 61 | +/* Master receives simpleclosed_got_sig: validate remote sig, store mutual |
| 62 | + * close tx, and reply with txid. drop_to_chain handles broadcast. */ |
| 63 | +static void handle_simpleclosed_got_sig(struct channel *channel, const u8 *msg) |
| 64 | +{ |
| 65 | + struct lightningd *ld = channel->peer->ld; |
| 66 | + struct bitcoin_tx *tx; |
| 67 | + struct bitcoin_txid txid; |
| 68 | + struct bitcoin_signature sig; |
| 69 | + const u8 *funding_wscript; |
| 70 | + |
| 71 | + if (!fromwire_simpleclosed_got_sig(tmpctx, msg, &tx, &sig)) { |
| 72 | + channel_internal_error(channel, "bad simpleclosed_got_sig: %s", |
| 73 | + tal_hex(msg, msg)); |
| 74 | + return; |
| 75 | + } |
| 76 | + tx->chainparams = chainparams; |
| 77 | + |
| 78 | + const char *err = close_tx_check(tmpctx, channel, tx); |
| 79 | + if (err) { |
| 80 | + channel_internal_error(channel, |
| 81 | + "bad simpleclosed_got_sig: %s", |
| 82 | + err); |
| 83 | + return; |
| 84 | + } |
| 85 | + |
| 86 | + funding_wscript = bitcoin_redeem_2of2(tmpctx, |
| 87 | + &channel->local_funding_pubkey, |
| 88 | + &channel->channel_info.remote_fundingkey); |
| 89 | + if (!check_tx_sig(tx, 0, NULL, funding_wscript, |
| 90 | + &channel->channel_info.remote_fundingkey, &sig)) { |
| 91 | + channel_internal_error(channel, |
| 92 | + "bad simpleclosed_got_sig: invalid sig: %s", |
| 93 | + tal_hex(msg, msg)); |
| 94 | + return; |
| 95 | + } |
| 96 | + |
| 97 | + channel_set_last_tx(channel, tx, &sig); |
| 98 | + wallet_channel_save(ld->wallet, channel); |
| 99 | + |
| 100 | + bitcoin_txid(tx, &txid); |
| 101 | + log_info(channel->log, |
| 102 | + "Simple close: stored closer tx %s", |
| 103 | + fmt_bitcoin_txid(tmpctx, &txid)); |
| 104 | + |
| 105 | + subd_send_msg(channel->owner, |
| 106 | + take(towire_simpleclosed_got_sig_reply(NULL, &txid))); |
| 107 | +} |
| 108 | + |
| 109 | +/* Master receives simpleclosed_closee_broadcast: validate remote sig and |
| 110 | + * store the mutual close tx. drop_to_chain handles broadcast. */ |
| 111 | +static void handle_simpleclosed_closee_broadcast(struct channel *channel, |
| 112 | + const u8 *msg) |
| 113 | +{ |
| 114 | + struct lightningd *ld = channel->peer->ld; |
| 115 | + struct bitcoin_tx *tx; |
| 116 | + struct bitcoin_txid txid; |
| 117 | + struct bitcoin_signature sig; |
| 118 | + const u8 *funding_wscript; |
| 119 | + |
| 120 | + if (!fromwire_simpleclosed_closee_broadcast(tmpctx, msg, &tx, &sig)) { |
| 121 | + channel_internal_error(channel, |
| 122 | + "bad simpleclosed_closee_broadcast: %s", |
| 123 | + tal_hex(msg, msg)); |
| 124 | + return; |
| 125 | + } |
| 126 | + tx->chainparams = chainparams; |
| 127 | + |
| 128 | + const char *err = close_tx_check(tmpctx, channel, tx); |
| 129 | + if (err) { |
| 130 | + channel_internal_error(channel, |
| 131 | + "bad simpleclosed_closee_broadcast: %s", |
| 132 | + err); |
| 133 | + return; |
| 134 | + } |
| 135 | + |
| 136 | + funding_wscript = bitcoin_redeem_2of2(tmpctx, |
| 137 | + &channel->local_funding_pubkey, |
| 138 | + &channel->channel_info.remote_fundingkey); |
| 139 | + if (!check_tx_sig(tx, 0, NULL, funding_wscript, |
| 140 | + &channel->channel_info.remote_fundingkey, &sig)) { |
| 141 | + channel_internal_error(channel, |
| 142 | + "bad simpleclosed_closee_broadcast: invalid sig: %s", |
| 143 | + tal_hex(msg, msg)); |
| 144 | + return; |
| 145 | + } |
| 146 | + |
| 147 | + channel_set_last_tx(channel, tx, &sig); |
| 148 | + wallet_channel_save(ld->wallet, channel); |
| 149 | + |
| 150 | + bitcoin_txid(tx, &txid); |
| 151 | + log_info(channel->log, |
| 152 | + "Simple close: stored closee tx %s", |
| 153 | + fmt_bitcoin_txid(tmpctx, &txid)); |
| 154 | +} |
| 155 | + |
| 156 | +static void handle_simpleclosed_complete(struct channel *channel, const u8 *msg) |
| 157 | +{ |
| 158 | + if (!fromwire_simpleclosed_complete(msg)) { |
| 159 | + channel_internal_error(channel, |
| 160 | + "bad simpleclosed_complete: %s", |
| 161 | + tal_hex(msg, msg)); |
| 162 | + return; |
| 163 | + } |
| 164 | + |
| 165 | + /* Don't report spurious failure when simpleclosed exits. */ |
| 166 | + channel_set_owner(channel, NULL); |
| 167 | + channel_set_billboard(channel, false, NULL); |
| 168 | + |
| 169 | + /* Retransmission only, ignore. */ |
| 170 | + if (channel->state != CLOSINGD_SIGEXCHANGE) |
| 171 | + return; |
| 172 | + |
| 173 | + channel_set_state(channel, |
| 174 | + CLOSINGD_SIGEXCHANGE, |
| 175 | + CLOSINGD_COMPLETE, |
| 176 | + REASON_UNKNOWN, |
| 177 | + "Simple close complete"); |
| 178 | + |
| 179 | + drop_to_chain(channel->peer->ld, channel, true, NULL); |
| 180 | +} |
| 181 | + |
| 182 | +static unsigned int simpleclosed_msg(struct subd *sd, const u8 *msg, |
| 183 | + const int *fds UNUSED) |
| 184 | +{ |
| 185 | + enum simpleclosed_wire t = fromwire_peektype(msg); |
| 186 | + |
| 187 | + switch (t) { |
| 188 | + case WIRE_SIMPLECLOSED_GOT_SIG: |
| 189 | + handle_simpleclosed_got_sig(sd->channel, msg); |
| 190 | + return 0; |
| 191 | + case WIRE_SIMPLECLOSED_CLOSEE_BROADCAST: |
| 192 | + handle_simpleclosed_closee_broadcast(sd->channel, msg); |
| 193 | + return 0; |
| 194 | + case WIRE_SIMPLECLOSED_COMPLETE: |
| 195 | + handle_simpleclosed_complete(sd->channel, msg); |
| 196 | + return 0; |
| 197 | + |
| 198 | + /* Inbound-only (master→daemon) — should not be received here. */ |
| 199 | + case WIRE_SIMPLECLOSED_INIT: |
| 200 | + case WIRE_SIMPLECLOSED_GOT_SIG_REPLY: |
| 201 | + break; |
| 202 | + } |
| 203 | + |
| 204 | + return 0; |
| 205 | +} |
| 206 | + |
| 207 | +void peer_start_simpleclosed(struct channel *channel, struct peer_fd *peer_fd) |
| 208 | +{ |
| 209 | + u8 *initmsg; |
| 210 | + u32 feerate_perkw; |
| 211 | + struct amount_msat their_msat; |
| 212 | + int hsmfd; |
| 213 | + struct lightningd *ld = channel->peer->ld; |
| 214 | + u32 *local_wallet_index = NULL; |
| 215 | + struct ext_key *local_wallet_ext_key = NULL; |
| 216 | + u32 index_val; |
| 217 | + struct ext_key ext_key_val; |
| 218 | + |
| 219 | + if (!channel->shutdown_scriptpubkey[REMOTE]) { |
| 220 | + channel_internal_error(channel, |
| 221 | + "Can't start simpleclosed: no remote script"); |
| 222 | + return; |
| 223 | + } |
| 224 | + |
| 225 | + hsmfd = hsm_get_client_fd(ld, &channel->peer->id, channel->dbid, |
| 226 | + HSM_PERM_SIGN_CLOSING_TX | HSM_PERM_COMMITMENT_POINT); |
| 227 | + if (hsmfd < 0) { |
| 228 | + log_broken(channel->log, |
| 229 | + "Could not get hsm fd for simpleclosed: %s", |
| 230 | + strerror(errno)); |
| 231 | + force_peer_disconnect(ld, channel->peer, |
| 232 | + "Failed to get hsm fd for simpleclosed"); |
| 233 | + return; |
| 234 | + } |
| 235 | + |
| 236 | + channel_set_owner(channel, |
| 237 | + new_channel_subd(channel, ld, "lightning_simpleclosed", channel, |
| 238 | + &channel->peer->id, channel->log, true, |
| 239 | + simpleclosed_wire_name, simpleclosed_msg, channel_errmsg, |
| 240 | + channel_set_billboard, take(&peer_fd->fd), take(&hsmfd), |
| 241 | + NULL)); |
| 242 | + |
| 243 | + if (!channel->owner) { |
| 244 | + log_broken(channel->log, |
| 245 | + "Could not subdaemon simpleclosed: %s", |
| 246 | + strerror(errno)); |
| 247 | + force_peer_disconnect(ld, channel->peer, |
| 248 | + "Failed to create simpleclosed"); |
| 249 | + return; |
| 250 | + } |
| 251 | + |
| 252 | + /* Compute their balance. */ |
| 253 | + if (!amount_sat_sub_msat(&their_msat, |
| 254 | + channel->funding_sats, channel->our_msat)) { |
| 255 | + log_broken(channel->log, |
| 256 | + "our_msat overflow on simple close: %s minus %s", |
| 257 | + fmt_amount_sat(tmpctx, channel->funding_sats), |
| 258 | + fmt_amount_msat(tmpctx, channel->our_msat)); |
| 259 | + channel_fail_permanent(channel, REASON_LOCAL, |
| 260 | + "our_msat overflow on simple close"); |
| 261 | + return; |
| 262 | + } |
| 263 | + |
| 264 | + feerate_perkw = mutual_close_feerate(ld->topology); |
| 265 | + if (!feerate_perkw) { |
| 266 | + feerate_perkw = get_feerate(channel->fee_states, |
| 267 | + channel->opener, LOCAL) / 2; |
| 268 | + if (feerate_perkw < get_feerate_floor(ld->topology)) |
| 269 | + feerate_perkw = get_feerate_floor(ld->topology); |
| 270 | + } |
| 271 | + |
| 272 | + /* Wallet key for our output. */ |
| 273 | + if (wallet_can_spend(ld->wallet, |
| 274 | + channel->shutdown_scriptpubkey[LOCAL], |
| 275 | + tal_bytelen(channel->shutdown_scriptpubkey[LOCAL]), |
| 276 | + &index_val, NULL)) { |
| 277 | + if (bip32_key_from_parent(ld->bip32_base, index_val, |
| 278 | + BIP32_FLAG_KEY_PUBLIC, |
| 279 | + &ext_key_val) != WALLY_OK) { |
| 280 | + channel_internal_error(channel, |
| 281 | + "Could not derive ext public key"); |
| 282 | + return; |
| 283 | + } |
| 284 | + local_wallet_index = &index_val; |
| 285 | + local_wallet_ext_key = &ext_key_val; |
| 286 | + } |
| 287 | + |
| 288 | + initmsg = towire_simpleclosed_init(tmpctx, chainparams, &channel->cid, |
| 289 | + &channel->funding, channel->funding_sats, |
| 290 | + &channel->local_funding_pubkey, |
| 291 | + &channel->channel_info.remote_fundingkey, |
| 292 | + amount_msat_to_sat_round_down(channel->our_msat), |
| 293 | + amount_msat_to_sat_round_down(their_msat), |
| 294 | + channel->our_config.dust_limit, feerate_perkw, local_wallet_index, |
| 295 | + local_wallet_ext_key, channel->shutdown_scriptpubkey[LOCAL], |
| 296 | + channel->shutdown_scriptpubkey[REMOTE], channel->opener); |
| 297 | + |
| 298 | + subd_send_msg(channel->owner, take(initmsg)); |
| 299 | +} |
0 commit comments