Skip to content

Commit

Permalink
onchaind: use lightningd to sign and broadcast htlc expired txs.
Browse files Browse the repository at this point in the history
This is when they closed the channel, we can simply make our own tx to
expire the HTLC.  (The other case is where we closed the channel, and
we have a special htlc_timeout tx which we have their signature for).

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Apr 7, 2023
1 parent 5bdd532 commit 0c27acc
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 181 deletions.
77 changes: 77 additions & 0 deletions lightningd/onchain_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,10 @@ struct onchain_signing_info {
struct pubkey remote_per_commitment_point;
struct preimage preimage;
} fulfill;
/* WIRE_ONCHAIND_SPEND_HTLC_EXPIRED */
struct {
struct pubkey remote_per_commitment_point;
} htlc_expired;
} u;
};

Expand Down Expand Up @@ -660,6 +664,22 @@ static u8 *sign_fulfill(const tal_t *ctx,
info->channel->dbid);
}

static u8 *sign_htlc_expired(const tal_t *ctx,
const struct bitcoin_tx *tx,
const struct onchain_signing_info *info)
{
const bool anchor_outputs = channel_has(info->channel, OPT_ANCHOR_OUTPUTS);

assert(info->msgtype == WIRE_ONCHAIND_SPEND_HTLC_EXPIRED);
return towire_hsmd_sign_any_remote_htlc_to_us(ctx,
&info->u.htlc_expired.remote_per_commitment_point,
tx, info->wscript,
anchor_outputs,
0,
&info->channel->peer->id,
info->channel->dbid);
}

/* Matches bitcoin_witness_sig_and_element! */
static const struct onchain_witness_element **
onchain_witness_sig_and_element(const tal_t *ctx, u8 **witness)
Expand Down Expand Up @@ -1172,6 +1192,59 @@ static void handle_onchaind_spend_htlc_timeout(struct channel *channel,
subd_send_msg(channel->owner, take(msg));
}

static void handle_onchaind_spend_htlc_expired(struct channel *channel,
const u8 *msg)
{
struct lightningd *ld = channel->peer->ld;
struct onchain_signing_info *info;
struct bitcoin_outpoint out;
struct amount_sat out_sats;
u64 htlc_id;
u32 cltv_expiry, initial_feerate;
const bool anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS);

info = new_signing_info(msg, channel, WIRE_ONCHAIND_SPEND_HTLC_EXPIRED);

/* BOLT #5:
*
* ## HTLC Output Handling: Remote Commitment, Local Offers
* ...
*
* - if the commitment transaction HTLC output has *timed out* AND NOT
* been *resolved*:
* - MUST *resolve* the output, by spending it to a convenient
* address.
*/
info->stack_elem = NULL;

if (!fromwire_onchaind_spend_htlc_expired(info, msg,
&out, &out_sats,
&htlc_id,
&cltv_expiry,
&info->u.htlc_expired.remote_per_commitment_point,
&info->wscript)) {
channel_internal_error(channel, "Invalid onchaind_spend_htlc_expired %s",
tal_hex(tmpctx, msg));
return;
}

/* nLocktime: we have to be *after* that block! */
info->minblock = cltv_expiry + 1;

/* FIXME: Be more sophisticated! */
initial_feerate = htlc_resolution_feerate(ld->topology);
if (!initial_feerate)
initial_feerate = tx_feerate(channel->last_tx);

/* We have to spend it before we can close incoming */
info->deadline_block = htlc_outgoing_incoming_deadline(channel, htlc_id);
create_onchain_tx(channel, &out, out_sats,
anchor_outputs ? 1 : 0,
cltv_expiry,
initial_feerate, sign_htlc_expired, info,
__func__);
}

static unsigned int onchain_msg(struct subd *sd, const u8 *msg, const int *fds UNUSED)
{
enum onchaind_wire t = fromwire_peektype(msg);
Expand Down Expand Up @@ -1241,6 +1314,10 @@ static unsigned int onchain_msg(struct subd *sd, const u8 *msg, const int *fds U
handle_onchaind_spend_fulfill(sd->channel, msg);
break;

case WIRE_ONCHAIND_SPEND_HTLC_EXPIRED:
handle_onchaind_spend_htlc_expired(sd->channel, msg);
break;

/* We send these, not receive them */
case WIRE_ONCHAIND_INIT:
case WIRE_ONCHAIND_SPENT:
Expand Down
122 changes: 14 additions & 108 deletions onchaind/onchaind.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,105 +607,6 @@ static struct amount_sat get_htlc_success_fee(struct tracked_output *out)
return fee;
}

static u8 *remote_htlc_to_us(const tal_t *ctx,
struct bitcoin_tx *tx,
const u8 *wscript)
{
return towire_hsmd_sign_remote_htlc_to_us(ctx,
remote_per_commitment_point,
tx, wscript,
option_anchor_outputs);
}

/*
* This covers:
* 1. to-us output spend (`<local_delayedsig> 0`)
* 2. the their-commitment, our HTLC timeout case (`<remotehtlcsig> 0`),
* 3. the their-commitment, our HTLC redeem case (`<remotehtlcsig> <payment_preimage>`)
* 4. the their-revoked-commitment, to-local (`<revocation_sig> 1`)
* 5. the their-revoked-commitment, htlc (`<revocation_sig> <revocationkey>`)
*
* Overrides *tx_type if it all turns to dust.
*/
static struct bitcoin_tx *tx_to_us(const tal_t *ctx,
u8 *(*hsm_sign_msg)(const tal_t *ctx,
struct bitcoin_tx *tx,
const u8 *wscript),
struct tracked_output *out,
u32 to_self_delay,
u32 locktime,
const void *elem, size_t elemsize,
const u8 *wscript,
enum tx_type *tx_type,
u32 feerate)
{
struct bitcoin_tx *tx;
struct amount_sat fee, min_out, amt;
struct bitcoin_signature sig;
size_t weight;
u8 *msg;
u8 **witness;

tx = bitcoin_tx(ctx, chainparams, 1, 1, locktime);
bitcoin_tx_add_input(tx, &out->outpoint, to_self_delay,
NULL, out->sat, NULL, wscript);

bitcoin_tx_add_output(
tx, scriptpubkey_p2wpkh(tmpctx, &our_wallet_pubkey), NULL, out->sat);
psbt_add_keypath_to_last_output(tx, our_wallet_index, &our_wallet_ext_key);

/* Worst-case sig is 73 bytes */
weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript);
weight += elements_tx_overhead(chainparams, 1, 1);
fee = amount_tx_fee(feerate, weight);

/* Result is trivial? Spend with small feerate, but don't wait
* around for it as it might not confirm. */
if (!amount_sat_add(&min_out, dust_limit, fee))
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Cannot add dust_limit %s and fee %s",
type_to_string(tmpctx, struct amount_sat, &dust_limit),
type_to_string(tmpctx, struct amount_sat, &fee));

if (amount_sat_less(out->sat, min_out)) {
/* FIXME: We should use SIGHASH_NONE so others can take it */
fee = amount_tx_fee(feerate_floor(), weight);
status_unusual("TX %s amount %s too small to"
" pay reasonable fee, using minimal fee"
" and ignoring",
tx_type_name(*tx_type),
type_to_string(tmpctx, struct amount_sat, &out->sat));
*tx_type = IGNORING_TINY_PAYMENT;
}

/* This can only happen if feerate_floor() is still too high; shouldn't
* happen! */
if (!amount_sat_sub(&amt, out->sat, fee)) {
amt = dust_limit;
status_broken("TX %s can't afford minimal feerate"
"; setting output to %s",
tx_type_name(*tx_type),
type_to_string(tmpctx, struct amount_sat,
&amt));
}
bitcoin_tx_output_set_amount(tx, 0, amt);
bitcoin_tx_finalize(tx);

if (!wire_sync_write(HSM_FD, take(hsm_sign_msg(NULL, tx, wscript))))
status_failed(STATUS_FAIL_HSM_IO, "Writing sign request to hsm");
msg = wire_sync_read(tmpctx, HSM_FD);
if (!msg || !fromwire_hsmd_sign_tx_reply(msg, &sig)) {
status_failed(STATUS_FAIL_HSM_IO,
"Reading sign_tx_reply: %s",
tal_hex(tmpctx, msg));
}

witness = bitcoin_witness_sig_and_element(tx, &sig, elem,
elemsize, wscript);
bitcoin_tx_input_set_witness(tx, 0, take(witness));
return tx;
}

static void hsm_get_per_commitment_point(struct pubkey *per_commitment_point)
{
u8 *msg = towire_hsmd_get_per_commitment_point(NULL, commit_num);
Expand Down Expand Up @@ -1944,6 +1845,7 @@ static void wait_for_resolved(struct tracked_output **outs)
case WIRE_ONCHAIND_SPEND_HTLC_SUCCESS:
case WIRE_ONCHAIND_SPEND_HTLC_TIMEOUT:
case WIRE_ONCHAIND_SPEND_FULFILL:
case WIRE_ONCHAIND_SPEND_HTLC_EXPIRED:
break;
}
master_badmsg(-1, msg);
Expand Down Expand Up @@ -2201,9 +2103,10 @@ static size_t resolve_our_htlc_theircommit(struct tracked_output *out,
const struct htlc_stub *htlcs,
u8 **htlc_scripts)
{
struct bitcoin_tx *tx;
enum tx_type tx_type = OUR_HTLC_TIMEOUT_TO_US;
const u8 *msg;
u32 cltv_expiry = matches_cltv(matches, htlcs);
/* They're all equivalent: might as well use first one. */
const struct htlc_stub *htlc = &htlcs[matches[0]];

/* BOLT #5:
*
Expand All @@ -2215,14 +2118,17 @@ static size_t resolve_our_htlc_theircommit(struct tracked_output *out,
* - MUST *resolve* the output, by spending it to a convenient
* address.
*/
tx = tx_to_us(out, remote_htlc_to_us, out,
option_anchor_outputs ? 1 : 0,
cltv_expiry, NULL, 0,
htlc_scripts[matches[0]], &tx_type, htlc_feerate);

propose_resolution_at_block(out, tx, cltv_expiry, tx_type);
msg = towire_onchaind_spend_htlc_expired(NULL,
&out->outpoint, out->sat,
htlc->id,
cltv_expiry,
remote_per_commitment_point,
htlc_scripts[matches[0]]);
propose_resolution_to_master(out, take(msg),
/* nLocktime: we have to be *after* that block! */
cltv_expiry + 1,
OUR_HTLC_TIMEOUT_TO_US);

/* They're all equivalent: might as well use first one. */
return matches[0];
}

Expand Down
11 changes: 11 additions & 0 deletions onchaind/onchaind_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,17 @@ msgdata,onchaind_spend_htlc_timeout,wscript,u8,wscript_len
msgdata,onchaind_spend_htlc_timeout,htlc_wscript_len,u32,
msgdata,onchaind_spend_htlc_timeout,htlc_wscript,u8,htlc_wscript_len

# We tell lightningd to create, sign and broadcast this tx to collect our
# expired htlc in their unilateral close:
msgtype,onchaind_spend_htlc_expired,5045
msgdata,onchaind_spend_htlc_expired,outpoint,bitcoin_outpoint,
msgdata,onchaind_spend_htlc_expired,outpoint_amount,amount_sat,
msgdata,onchaind_spend_htlc_expired,htlc_id,u64,
msgdata,onchaind_spend_htlc_expired,cltv_expiry,u32,
msgdata,onchaind_spend_htlc_expired,remote_per_commitment_point,pubkey,
msgdata,onchaind_spend_htlc_expired,wscript_len,u32,
msgdata,onchaind_spend_htlc_expired,wscript,u8,wscript_len

subtype,onchain_witness_element
subtypedata,onchain_witness_element,is_signature,bool,
subtypedata,onchain_witness_element,len,u32,
Expand Down
3 changes: 0 additions & 3 deletions onchaind/test/run-grind_feerate-bug.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,6 @@ void towire_ext_key(u8 **pptr UNNEEDED, const struct ext_key *bip32 UNNEEDED)
/* Generated stub for towire_hsmd_get_per_commitment_point */
u8 *towire_hsmd_get_per_commitment_point(const tal_t *ctx UNNEEDED, u64 n UNNEEDED)
{ fprintf(stderr, "towire_hsmd_get_per_commitment_point called!\n"); abort(); }
/* Generated stub for towire_hsmd_sign_remote_htlc_to_us */
u8 *towire_hsmd_sign_remote_htlc_to_us(const tal_t *ctx UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, bool option_anchor_outputs UNNEEDED)
{ fprintf(stderr, "towire_hsmd_sign_remote_htlc_to_us called!\n"); abort(); }
/* Generated stub for towire_htlc_stub */
void towire_htlc_stub(u8 **pptr UNNEEDED, const struct htlc_stub *htlc_stub UNNEEDED)
{ fprintf(stderr, "towire_htlc_stub called!\n"); abort(); }
Expand Down
9 changes: 3 additions & 6 deletions onchaind/test/run-grind_feerate.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
/* Generated stub for fromwire_hsmd_get_per_commitment_point_reply */
bool fromwire_hsmd_get_per_commitment_point_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct pubkey *per_commitment_point UNNEEDED, struct secret **old_commitment_secret UNNEEDED)
{ fprintf(stderr, "fromwire_hsmd_get_per_commitment_point_reply called!\n"); abort(); }
/* Generated stub for fromwire_hsmd_sign_tx_reply */
bool fromwire_hsmd_sign_tx_reply(const void *p UNNEEDED, struct bitcoin_signature *sig UNNEEDED)
{ fprintf(stderr, "fromwire_hsmd_sign_tx_reply called!\n"); abort(); }
/* Generated stub for fromwire_onchaind_depth */
bool fromwire_onchaind_depth(const void *p UNNEEDED, struct bitcoin_txid *txid UNNEEDED, u32 *depth UNNEEDED)
{ fprintf(stderr, "fromwire_onchaind_depth called!\n"); abort(); }
Expand Down Expand Up @@ -272,9 +269,6 @@ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED)
/* Generated stub for towire_hsmd_get_per_commitment_point */
u8 *towire_hsmd_get_per_commitment_point(const tal_t *ctx UNNEEDED, u64 n UNNEEDED)
{ fprintf(stderr, "towire_hsmd_get_per_commitment_point called!\n"); abort(); }
/* Generated stub for towire_hsmd_sign_remote_htlc_to_us */
u8 *towire_hsmd_sign_remote_htlc_to_us(const tal_t *ctx UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, bool option_anchor_outputs UNNEEDED)
{ fprintf(stderr, "towire_hsmd_sign_remote_htlc_to_us called!\n"); abort(); }
/* Generated stub for towire_onchaind_add_utxo */
u8 *towire_onchaind_add_utxo(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *prev_out UNNEEDED, const struct pubkey *per_commit_point UNNEEDED, struct amount_sat value UNNEEDED, u32 blockheight UNNEEDED, const u8 *scriptpubkey UNNEEDED, u32 csv_lock UNNEEDED)
{ fprintf(stderr, "towire_onchaind_add_utxo called!\n"); abort(); }
Expand Down Expand Up @@ -311,6 +305,9 @@ u8 *towire_onchaind_notify_coin_mvt(const tal_t *ctx UNNEEDED, const struct chai
/* Generated stub for towire_onchaind_spend_fulfill */
u8 *towire_onchaind_spend_fulfill(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, struct amount_sat outpoint_amount UNNEEDED, u64 htlc_id UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const struct preimage *preimage UNNEEDED, const u8 *wscript UNNEEDED)
{ fprintf(stderr, "towire_onchaind_spend_fulfill called!\n"); abort(); }
/* Generated stub for towire_onchaind_spend_htlc_expired */
u8 *towire_onchaind_spend_htlc_expired(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, struct amount_sat outpoint_amount UNNEEDED, u64 htlc_id UNNEEDED, u32 cltv_expiry UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const u8 *wscript UNNEEDED)
{ fprintf(stderr, "towire_onchaind_spend_htlc_expired called!\n"); abort(); }
/* Generated stub for towire_onchaind_spend_htlc_success */
u8 *towire_onchaind_spend_htlc_success(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, struct amount_sat outpoint_amount UNNEEDED, struct amount_sat fee UNNEEDED, u64 htlc_id UNNEEDED, u64 commit_num UNNEEDED, const struct bitcoin_signature *remote_htlc_sig UNNEEDED, const struct preimage *preimage UNNEEDED, const u8 *wscript UNNEEDED, const u8 *htlc_wscript UNNEEDED)
{ fprintf(stderr, "towire_onchaind_spend_htlc_success called!\n"); abort(); }
Expand Down
Loading

0 comments on commit 0c27acc

Please sign in to comment.