cln/closingd/simpleclosed.c
Níckolas Goline da67bf845f
simpleclosed.c: add heuristic to delay our tx broadcast if our amount is less AND our fee is less than our peer's amount and fee
in case of a reboot the tx will be broadcast as usual.

simple_close_control.c: add delay logic to `drop_to_chain` after 1 hour.
test_closing.py: add test to verify the heuristic is being applied.
2026-07-02 13:30:17 -03:00

791 lines
28 KiB
C

/* Main simple-close daemon: implements option_simple_close (BOLT #2).
* Runs after channeld signals shutdown_complete, replacing closingd
* when option_simple_close is negotiated. */
#include "config.h"
#include <bitcoin/pubkey.h>
#include <bitcoin/script.h>
#include <bitcoin/signature.h>
#include <ccan/cast/cast.h>
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <closingd/simpleclosed_wiregen.h>
#include <common/close_tx.h>
#include <common/memleak.h>
#include <common/peer_billboard.h>
#include <common/peer_failed.h>
#include <common/peer_io.h>
#include <common/per_peer_state.h>
#include <common/read_peer_msg.h>
#include <common/shutdown_scriptpubkey.h>
#include <common/status.h>
#include <common/subdaemon.h>
#include <common/utils.h>
#include <errno.h>
#include <hsmd/hsmd_wiregen.h>
#include <inttypes.h>
#include <stdio.h>
#include <unistd.h>
#include <wally_script.h>
#include <wire/peer_wire.h>
#include <wire/wire_sync.h>
/* stdin == requests from master, 3 == peer fd, 4 == hsmd fd */
#define REQ_FD STDIN_FILENO
#define PEER_FD 3
#define HSM_FD 4
/* Approx weight of a simple-close tx with both outputs (vbytes * 4 for weight).
* Input: 41vb, witness: ~222wu/4=55.5vb, outputs: ~65vb each, overhead: 11vb
* Total ~236 vbytes = ~704 weight + witness ~222 = ~926wu, round to 900. */
#define SIMPLE_CLOSE_WEIGHT 900
static const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES)
{
u8 *msg;
if (!wire_sync_write(HSM_FD, req))
status_failed(STATUS_FAIL_HSM_IO,
"Writing to HSM: %s",
strerror(errno));
msg = wire_sync_read(ctx, HSM_FD);
if (!msg)
status_failed(STATUS_FAIL_HSM_IO,
"Reading from HSM: %s",
strerror(errno));
return msg;
}
static struct per_peer_state *pps;
/* Tell master we got peer's closing_sig for our tx; block for txid reply. */
static struct bitcoin_txid master_got_sig(struct bitcoin_tx *tx,
const struct bitcoin_signature *sig)
{
struct bitcoin_txid txid;
u8 *msg;
msg = towire_simpleclosed_got_sig(tmpctx, tx, sig);
if (!wire_sync_write(REQ_FD, take(msg)))
status_failed(STATUS_FAIL_MASTER_IO,
"Writing got_sig: %s",
strerror(errno));
msg = wire_sync_read(tmpctx, REQ_FD);
if (!msg)
status_failed(STATUS_FAIL_MASTER_IO,
"Reading got_sig_reply: %s",
strerror(errno));
if (!fromwire_simpleclosed_got_sig_reply(msg, &txid))
status_failed(STATUS_FAIL_MASTER_IO,
"Bad got_sig_reply: %s",
tal_hex(tmpctx, msg));
return txid;
}
/* Build one closing tx variant. closer_script or closee_script may be NULL
* to omit that output. */
static struct bitcoin_tx *make_close_tx(const tal_t *ctx,
u32 *local_wallet_index,
const struct ext_key *local_wallet_ext_key,
const u8 *closer_script,
const u8 *closee_script,
const u8 *funding_wscript,
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
struct amount_sat closer_amount,
struct amount_sat closee_amount,
u32 locktime)
{
return create_simple_close_tx(ctx, local_wallet_index, local_wallet_ext_key,
closer_script, closee_script, funding_wscript, funding, funding_sats,
closer_amount, closee_amount, locktime);
}
/* Sign a simple close tx via HSM; returns bitcoin_signature. */
static struct bitcoin_signature sign_tx(const tal_t *ctx,
struct bitcoin_tx *tx,
const struct pubkey *remote_fundingkey)
{
struct bitcoin_signature sig;
const u8 *msg;
msg = hsm_req(tmpctx,
take(towire_hsmd_sign_mutual_close_tx(NULL, tx, remote_fundingkey)));
if (!fromwire_hsmd_sign_tx_reply(msg, &sig))
status_failed(STATUS_FAIL_HSM_IO,
"Bad sign_mutual_close_tx reply %s",
tal_hex(tmpctx, msg));
return sig;
}
/* Build and send closing_complete as the closer.
* Returns the TLVs we sent (so we can validate the closing_sig reply).
* Also writes the computed fee into *fee_out. */
static struct tlv_closing_tlvs *send_closing_complete(
const struct channel_id *channel_id,
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
const struct pubkey *local_fundingkey,
const struct pubkey *remote_fundingkey,
u32 *local_wallet_index,
const struct ext_key *local_wallet_ext_key,
struct amount_sat local_sat,
struct amount_sat remote_sat,
struct amount_sat dust_limit,
u32 feerate_perkw,
const u8 *local_script,
const u8 *remote_script,
u32 locktime,
struct amount_sat *fee_out)
{
const u8 *funding_wscript;
struct amount_sat fee, closer_amount, closee_amount;
bool is_lesser, closee_dust, closer_dust;
struct bitcoin_tx *tx_both, *tx_closer_only, *tx_closee_only;
struct bitcoin_signature sig;
struct tlv_closing_tlvs *tlvs;
u8 *msg;
funding_wscript = bitcoin_redeem_2of2(tmpctx,
local_fundingkey, remote_fundingkey);
/* Fee: feerate * weight / 1000, capped at our balance. */
fee = amount_sat((u64)feerate_perkw * SIMPLE_CLOSE_WEIGHT / 1000);
if (amount_sat_greater(fee, local_sat))
fee = local_sat; /* Can't pay more than we have */
if (!amount_sat_sub(&closer_amount, local_sat, fee))
closer_amount = AMOUNT_SAT(0);
closee_amount = remote_sat;
is_lesser = amount_sat_less(local_sat, remote_sat);
closee_dust = amount_sat_less(closee_amount, dust_limit)
&& !is_valid_op_return(remote_script, tal_bytelen(remote_script));
closer_dust = amount_sat_less(closer_amount, dust_limit)
&& !is_valid_op_return(local_script, tal_bytelen(local_script));
tlvs = tlv_closing_tlvs_new(tmpctx);
/* BOLT #2:
* The sender of `closing_complete` (aka. "the closer"):
* ...
* - If the local outstanding balance (in millisatoshi) is less than the remote outstanding balance:
* - MUST NOT set `closer_output_only`.
*/
if (is_lesser) {
/* BOLT #2:
* ...
* - MUST set `closee_output_only` if the local output amount is dust.
* - MAY set `closee_output_only` if it considers the local output amount uneconomical AND its `closer_scriptpubkey` is not `OP_RETURN`.
*/
if (closer_dust) {
/* Our output is dust: sign tx with only closee output */
tx_closee_only = make_close_tx(tmpctx, NULL, NULL, NULL,
remote_script, funding_wscript, funding, funding_sats,
AMOUNT_SAT(0), closee_amount, locktime);
if (tx_closee_only) {
sig = sign_tx(tmpctx, tx_closee_only, remote_fundingkey);
tlvs->closee_output_only = tal(tlvs, secp256k1_ecdsa_signature);
*tlvs->closee_output_only = sig.s;
}
} else {
/* Both outputs present; closee can use this. */
tx_both = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, local_script, remote_script,
funding_wscript, funding, funding_sats, closer_amount,
closee_amount, locktime);
if (tx_both) {
sig = sign_tx(tmpctx, tx_both, remote_fundingkey);
tlvs->closer_and_closee_outputs = tal(tlvs,
secp256k1_ecdsa_signature);
*tlvs->closer_and_closee_outputs = sig.s;
}
}
} else {
/* BOLT #2:
* - Otherwise (not lesser amount, cannot remove its own output):
* - MUST NOT set `closee_output_only`.
* - If it considers the local output amount uneconomical:
* - MAY send a `closer_scriptpubkey` that is a valid `OP_RETURN` script.
* - If it does, the output value MUST be set to zero so that all funds go to fees, as specified in [BOLT #3]...
*/
tx_closer_only = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, local_script, NULL, funding_wscript, funding,
funding_sats, closer_amount, AMOUNT_SAT(0), locktime);
if (tx_closer_only) {
sig = sign_tx(tmpctx, tx_closer_only, remote_fundingkey);
tlvs->closer_output_only = tal(tlvs, secp256k1_ecdsa_signature);
*tlvs->closer_output_only = sig.s;
}
/* BOLT #2:
* - If the closee's output amount is dust:
* - MUST set `closer_output_only`.
* - MUST NOT set `closer_and_closee_outputs`.
* - Otherwise:
* - MUST set both `closer_output_only` and `closer_and_closee_outputs`.
*/
if (!closee_dust) {
/* Also sign the both-outputs variant. */
tx_both = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, local_script, remote_script,
funding_wscript, funding, funding_sats, closer_amount,
closee_amount, locktime);
if (tx_both) {
sig = sign_tx(tmpctx, tx_both, remote_fundingkey);
tlvs->closer_and_closee_outputs = tal(tlvs,
secp256k1_ecdsa_signature);
*tlvs->closer_and_closee_outputs = sig.s;
}
}
}
/* Sanity: must have at least one sig to send. */
if (!tlvs->closer_output_only && !tlvs->closee_output_only
&& !tlvs->closer_and_closee_outputs)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"No valid closing tx variant could be built "
"(local=%s remote=%s fee=%s dust=%s)",
fmt_amount_sat(tmpctx, local_sat),
fmt_amount_sat(tmpctx, remote_sat),
fmt_amount_sat(tmpctx, fee),
fmt_amount_sat(tmpctx, dust_limit));
msg = towire_closing_complete(tmpctx, channel_id, local_script,
remote_script, fee, locktime, tlvs);
peer_write(pps, take(msg));
*fee_out = fee;
status_debug("Sent closing_complete: closer=%s closee=%s fee=%s",
fmt_amount_sat(tmpctx, closer_amount),
fmt_amount_sat(tmpctx, closee_amount),
fmt_amount_sat(tmpctx, fee));
return tlvs;
}
static struct bitcoin_signature check_their_sig(
const secp256k1_ecdsa_signature *raw_sig,
const struct bitcoin_tx *tx,
const u8 *funding_wscript,
const struct pubkey *remote_fundingkey,
const struct channel_id *their_cid,
const char *whatfield)
{
struct bitcoin_signature sig;
sig.sighash_type = SIGHASH_ALL;
sig.s = *raw_sig;
if (!check_tx_sig(tx, 0, NULL, funding_wscript, remote_fundingkey, &sig))
peer_failed_warn(pps, their_cid,
"Bad %s sig: tx=%s funding_wscript=%s fundingkey=%s",
whatfield,
fmt_bitcoin_tx(tmpctx, tx),
tal_hex(tmpctx, funding_wscript),
fmt_pubkey(tmpctx, remote_fundingkey));
return sig;
}
static secp256k1_ecdsa_signature *our_sig(const tal_t *ctx,
struct bitcoin_tx *tx,
const struct pubkey *remote_fundingkey,
struct bitcoin_signature *sig_out,
const char *whatfield)
{
secp256k1_ecdsa_signature *result;
*sig_out = sign_tx(tmpctx, tx, remote_fundingkey);
result = tal(ctx, secp256k1_ecdsa_signature);
*result = sig_out->s;
status_debug("Signed %s: tx=%s", whatfield, fmt_bitcoin_tx(tmpctx, tx));
return result;
}
/* As closee: receive peer's closing_complete, sign the right variant,
* send closing_sig, and broadcast. Returns the tx we signed. */
static struct bitcoin_tx *handle_closing_complete(
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
const struct pubkey *local_fundingkey,
const struct pubkey *remote_fundingkey,
u32 *local_wallet_index,
const struct ext_key *local_wallet_ext_key,
struct amount_sat local_sat,
struct amount_sat dust_limit,
const u8 *our_last_script,
const u8 *msg,
struct amount_sat *their_fee_out)
{
struct channel_id their_cid = {};
u8 *closer_script, *closee_script;
struct amount_sat fee_sat;
u32 locktime;
struct tlv_closing_tlvs *tlvs;
struct tlv_closing_tlvs *reply_tlvs;
struct bitcoin_tx *chosen_tx;
struct bitcoin_signature sig;
struct amount_sat closer_amount, closee_amount;
const u8 *funding_wscript;
u8 *reply;
bool anysegwit = true, option_simple_close = true;
struct amount_sat remote_sat;
if (!fromwire_closing_complete(tmpctx, msg, &their_cid, &closer_script,
&closee_script, &fee_sat, &locktime, &tlvs))
peer_failed_warn(pps, &their_cid,
"Bad closing_complete: %s",
tal_hex(tmpctx, msg));
*their_fee_out = fee_sat;
/* BOLT #2:
* The receiver of `closing_complete` (aka. "the closee"):
* ...
* - If `closee_scriptpubkey` does not match the last script it sent (from `closing_complete` or from the initial `shutdown`):
* - SHOULD ignore `closing_complete`.
* - SHOULD send a `warning`.
* - SHOULD close the connection.
*/
if (!scripteq(closee_script, our_last_script)) {
peer_failed_warn(pps, &their_cid,
"closing_complete closee_script %s != our script %s: "
"reconnect to resync",
tal_hex(tmpctx, closee_script),
tal_hex(tmpctx, our_last_script));
}
/* BOLT #2:
* The receiver of `closing_complete` (aka. "the closee"):
* ...
* - If `closer_scriptpubkey` is invalid (as detailed in the [`shutdown` requirements](#closing-initiation-shutdown)):
* - SHOULD ignore `closing_complete`.
* - SHOULD send a `warning`.
* - SHOULD close the connection.
*/
if (!valid_shutdown_scriptpubkey(closer_script, anysegwit, false,
option_simple_close))
peer_failed_warn(pps, &their_cid,
"Invalid closer_scriptpubkey in closing_complete: %s",
tal_hex(tmpctx, closer_script));
funding_wscript = bitcoin_redeem_2of2(tmpctx, local_fundingkey,
remote_fundingkey);
/* Compute amounts: closer pays fee, closee gets remote_sat. */
if (!amount_sat_sub(&remote_sat, funding_sats, local_sat))
remote_sat = AMOUNT_SAT(0);
/* BOLT #2:
* The receiver of `closing_complete` (aka. "the closee"):
* - If `fee_satoshis` is greater than the closer's outstanding balance:
* - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
*/
if (is_valid_op_return(closer_script, tal_bytelen(closer_script)))
closer_amount = AMOUNT_SAT(0);
else if (!amount_sat_sub(&closer_amount, remote_sat, fee_sat))
peer_failed_warn(pps, &their_cid, "Closer fee %s exceeds their balance %s",
fmt_amount_sat(tmpctx, fee_sat),
fmt_amount_sat(tmpctx, remote_sat));
if (is_valid_op_return(closee_script, tal_bytelen(closee_script)))
closee_amount = AMOUNT_SAT(0);
else
closee_amount = local_sat;
bool our_output_dust = amount_sat_less(closee_amount, dust_limit)
&& !is_valid_op_return(closee_script, tal_bytelen(closee_script));
reply_tlvs = tlv_closing_tlvs_new(tmpctx);
struct bitcoin_signature their_sig;
/* BOLT #2:
* The receiver of `closing_complete` (aka. "the closee"):
* ...
* - Select a signature for validation:
* - If the local output amount is dust:
* - MUST use `closer_output_only`.
* - Otherwise, if it considers the local output amount uneconomical AND its `closee_scriptpubkey` is not `OP_RETURN`:
* - MUST use `closer_output_only`.
* - Otherwise, if `closer_and_closee_outputs` is present:
* - MUST use `closer_and_closee_outputs`.
* - Otherwise:
* - MUST use `closee_output_only`.
* - If the selected signature field does not exist:
* - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
* - If the signature field is not valid for the corresponding closing transaction specified in [BOLT #3]...:
* - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
*/
if (our_output_dust) {
if (!tlvs->closer_output_only)
peer_failed_warn(pps, &their_cid,
"Our output is dust but no "
"closer_output_only sig provided");
chosen_tx = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, closer_script, NULL, funding_wscript,
funding, funding_sats, closer_amount, AMOUNT_SAT(0), locktime);
reply_tlvs->closer_output_only = our_sig(reply_tlvs, chosen_tx,
remote_fundingkey, &sig, "closer_output_only");
their_sig = check_their_sig(tlvs->closer_output_only, chosen_tx,
funding_wscript, remote_fundingkey, &their_cid,
"closer_output_only");
} else if (tlvs->closer_and_closee_outputs) {
chosen_tx = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, closer_script, closee_script,
funding_wscript, funding, funding_sats, closer_amount,
closee_amount, locktime);
reply_tlvs->closer_and_closee_outputs = our_sig(reply_tlvs, chosen_tx,
remote_fundingkey, &sig, "closer_and_closee_outputs");
their_sig = check_their_sig(tlvs->closer_and_closee_outputs, chosen_tx,
funding_wscript, remote_fundingkey, &their_cid,
"closer_and_closee_outputs");
} else {
if (!tlvs->closee_output_only)
peer_failed_warn(pps, &their_cid, "No valid sig variant in "
"closing_complete");
chosen_tx = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, NULL, closee_script, funding_wscript,
funding, funding_sats, AMOUNT_SAT(0), closee_amount, locktime);
reply_tlvs->closee_output_only = our_sig(reply_tlvs, chosen_tx,
remote_fundingkey, &sig, "closee_output_only");
their_sig = check_their_sig(tlvs->closee_output_only, chosen_tx,
funding_wscript, remote_fundingkey, &their_cid,
"closee_output_only");
}
/* BOLT #2:
* The receiver of `closing_complete` (aka. "the closee"):
* ...
* - MUST send `closing_sig` with a single valid signature in the same TLV field as the `closing_complete`.
*/
reply = towire_closing_sig(tmpctx, &their_cid, closer_script,
closee_script, fee_sat, locktime, reply_tlvs);
peer_write(pps, take(reply));
status_debug("Sent closing_sig (as closee), notifying master");
/* Tell master to validate, store, and handle broadcast. */
wire_sync_write(REQ_FD,
take(towire_simpleclosed_closee_broadcast(NULL,
chosen_tx, &their_sig)));
return chosen_tx;
}
/* As closer: validate the closing_sig we got back.
* Returns the closing tx; writes peer's sig to *their_sig_out. */
static struct bitcoin_tx *handle_closing_sig(
const struct bitcoin_outpoint *funding,
struct amount_sat funding_sats,
const struct pubkey *local_fundingkey,
const struct pubkey *remote_fundingkey,
u32 *local_wallet_index,
const struct ext_key *local_wallet_ext_key,
struct amount_sat local_sat,
struct amount_sat remote_sat,
struct amount_sat dust_limit,
const u8 *sent_closer_script,
const u8 *sent_closee_script,
struct amount_sat sent_fee,
u32 sent_locktime,
const struct tlv_closing_tlvs *sent_tlvs,
const u8 *msg,
struct bitcoin_signature *their_sig_out)
{
struct channel_id their_cid = {};
u8 *closer_script, *closee_script;
struct amount_sat fee_sat;
u32 locktime;
struct tlv_closing_tlvs *tlvs;
const u8 *funding_wscript;
struct amount_sat closer_amount, closee_amount;
if (!fromwire_closing_sig(tmpctx, msg, &their_cid,&closer_script,
&closee_script, &fee_sat, &locktime, &tlvs))
peer_failed_warn(pps, &their_cid,
"Bad closing_sig: %s",
tal_hex(tmpctx, msg));
/* BOLT #2:
* The receiver of `closing_sig`:
* - If `closer_scriptpubkey`, `closee_scriptpubkey`, `fee_satoshis` or `locktime` don't match what was sent in `closing_complete`:
* - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
*/
if (!scripteq(closer_script, sent_closer_script)
|| !scripteq(closee_script, sent_closee_script)
|| !amount_sat_eq(fee_sat, sent_fee)
|| locktime != sent_locktime)
peer_failed_warn(pps, &their_cid, "closing_sig fields don't match our "
"closing_complete (script/fee/locktime mismatch)");
/* BOLT #2:
* The receiver of `closing_sig`:
* ...
* - If `tlvs` does not contain exactly one signature:
* - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
*/
int nsigs = (tlvs->closer_output_only ? 1 : 0)
+ (tlvs->closee_output_only ? 1 : 0)
+ (tlvs->closer_and_closee_outputs ? 1 : 0);
if (nsigs != 1)
peer_failed_warn(pps, &their_cid,
"closing_sig must have exactly one sig, got %d",
nsigs);
if (tlvs->closer_output_only && !sent_tlvs->closer_output_only)
peer_failed_warn(pps, &their_cid,
"closing_sig closer_output_only not in our "
"closing_complete");
if (tlvs->closee_output_only && !sent_tlvs->closee_output_only)
peer_failed_warn(pps, &their_cid,
"closing_sig closee_output_only not in our "
"closing_complete");
if (tlvs->closer_and_closee_outputs
&& !sent_tlvs->closer_and_closee_outputs)
peer_failed_warn(pps, &their_cid,
"closing_sig closer_and_closee_outputs not in "
"our closing_complete");
funding_wscript = bitcoin_redeem_2of2(tmpctx, local_fundingkey,
remote_fundingkey);
if (is_valid_op_return(closer_script, tal_bytelen(closer_script)))
closer_amount = AMOUNT_SAT(0);
else if (!amount_sat_sub(&closer_amount, local_sat, fee_sat))
closer_amount = AMOUNT_SAT(0);
if (is_valid_op_return(closee_script, tal_bytelen(closee_script)))
closee_amount = AMOUNT_SAT(0);
else
closee_amount = remote_sat;
/* Reconstruct the tx that was signed. */
struct bitcoin_tx *signed_tx;
struct bitcoin_signature their_sig;
their_sig.sighash_type = SIGHASH_ALL;
if (tlvs->closer_output_only) {
their_sig.s = *tlvs->closer_output_only;
signed_tx = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, closer_script, NULL, funding_wscript,
funding, funding_sats, closer_amount, AMOUNT_SAT(0), locktime);
} else if (tlvs->closee_output_only) {
their_sig.s = *tlvs->closee_output_only;
signed_tx = make_close_tx(tmpctx, NULL, NULL, NULL, closee_script,
funding_wscript, funding, funding_sats, AMOUNT_SAT(0),
closee_amount, locktime);
} else {
their_sig.s = *tlvs->closer_and_closee_outputs;
signed_tx = make_close_tx(tmpctx, local_wallet_index,
local_wallet_ext_key, closer_script, closee_script,
funding_wscript, funding, funding_sats, closer_amount,
closee_amount, locktime);
}
/* BOLT #2:
* The receiver of `closing_sig`:
* ...
* - If the signature field is not valid for the corresponding closing transaction specified in [BOLT #3]...:
* - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
*/
if (!check_tx_sig(signed_tx, 0, NULL, funding_wscript,
remote_fundingkey, &their_sig))
peer_failed_warn(pps, &their_cid,
"Bad signature in closing_sig: tx=%s funding_wscript=%s fundingkey=%s",
fmt_bitcoin_tx(tmpctx, signed_tx),
tal_hex(tmpctx, funding_wscript),
fmt_pubkey(tmpctx, remote_fundingkey));
status_debug("closing_sig verified OK, sending to master");
*their_sig_out = their_sig;
return signed_tx;
}
int main(int argc, char *argv[])
{
setup_locale();
const tal_t *ctx = tal(NULL, char);
u8 *msg;
struct pubkey local_fundingkey, remote_fundingkey;
struct bitcoin_outpoint funding;
struct amount_sat funding_sats, local_sat, remote_sat, dust_limit;
u32 feerate_perkw;
u32 *local_wallet_index;
struct ext_key *local_wallet_ext_key;
u8 *local_script, *remote_script;
struct channel_id channel_id;
enum side opener;
bool got_peer_complete, got_our_sig;
struct tlv_closing_tlvs *sent_tlvs;
u8 *sent_closer_script, *sent_closee_script;
struct amount_sat sent_fee, their_fee;
u32 sent_locktime = 0;
subdaemon_setup(argc, argv);
status_setup_sync(REQ_FD);
msg = wire_sync_read(tmpctx, REQ_FD);
if (!fromwire_simpleclosed_init(ctx, msg, &chainparams, &channel_id,
&funding, &funding_sats, &local_fundingkey, &remote_fundingkey,
&local_sat, &remote_sat, &dust_limit, &feerate_perkw,
&local_wallet_index, &local_wallet_ext_key, &local_script,
&remote_script, &opener))
status_failed(STATUS_FAIL_MASTER_IO,
"Bad simpleclosed_init: %s",
tal_hex(tmpctx, msg));
/* stdin == requests, 3 == peer, 4 = hsmd */
pps = notleak(new_per_peer_state(ctx));
per_peer_state_set_fd(pps, PEER_FD);
status_debug("Simple close starting: local=%s remote=%s",
fmt_amount_sat(tmpctx, local_sat),
fmt_amount_sat(tmpctx, remote_sat));
/* Send our own closing_complete as the closer. */
sent_closer_script = local_script;
sent_closee_script = remote_script;
sent_locktime = 0; /* nLockTime = 0 for initial proposal */
sent_tlvs = send_closing_complete(&channel_id, &funding, funding_sats,
&local_fundingkey, &remote_fundingkey, local_wallet_index,
local_wallet_ext_key, local_sat, remote_sat, dust_limit,
feerate_perkw, local_script, remote_script, sent_locktime,
&sent_fee);
/* sent_tlvs is allocated on tmpctx inside send_closing_complete; steal
* it onto ctx so it survives clean_tmpctx() calls in the exchange loop. */
tal_steal(ctx, sent_tlvs);
/* Exchange loop: wait for peer's closing_complete and our closing_sig. */
got_peer_complete = false;
got_our_sig = false;
while (!got_peer_complete || !got_our_sig) {
clean_tmpctx();
msg = peer_read(tmpctx, pps);
switch (fromwire_peektype(msg)) {
case WIRE_CLOSING_COMPLETE:
/* Peer sends us their closing_complete: we are the closee. */
if (got_peer_complete) {
status_debug("Got another closing_complete "
"(peer bumping fee): re-signing");
}
handle_closing_complete(&funding, funding_sats,
&local_fundingkey, &remote_fundingkey, local_wallet_index,
local_wallet_ext_key, local_sat, dust_limit, local_script,
msg, &their_fee);
got_peer_complete = true;
break;
case WIRE_CLOSING_SIG: {
/* Peer replies to our closing_complete: we are the closer. */
struct bitcoin_tx *closing_tx;
struct bitcoin_txid txid;
struct bitcoin_signature their_sig;
closing_tx = handle_closing_sig(&funding,
funding_sats, &local_fundingkey, &remote_fundingkey,
local_wallet_index, local_wallet_ext_key, local_sat,
remote_sat, dust_limit, sent_closer_script, sent_closee_script,
sent_fee, sent_locktime, sent_tlvs, msg, &their_sig);
txid = master_got_sig(closing_tx, &their_sig);
status_debug("Closer tx stored by master: %s",
fmt_bitcoin_txid(tmpctx, &txid));
got_our_sig = true;
break;
}
case WIRE_CLOSING_SIGNED:
/* Peer is still running legacy close — shouldn't happen
* if option_simple_close was negotiated. */
peer_failed_warn(pps, &channel_id,
"Got legacy closing_signed but "
"option_simple_close was negotiated");
break;
/* These are all swallowed by connectd */
case WIRE_PROTOCOL_BATCH_ELEMENT:
case WIRE_CHANNEL_ANNOUNCEMENT:
case WIRE_CHANNEL_UPDATE:
case WIRE_NODE_ANNOUNCEMENT:
case WIRE_QUERY_SHORT_CHANNEL_IDS:
case WIRE_QUERY_CHANNEL_RANGE:
case WIRE_REPLY_CHANNEL_RANGE:
case WIRE_GOSSIP_TIMESTAMP_FILTER:
case WIRE_REPLY_SHORT_CHANNEL_IDS_END:
case WIRE_PING:
case WIRE_PONG:
case WIRE_WARNING:
case WIRE_ERROR:
case WIRE_ONION_MESSAGE:
case WIRE_PEER_STORAGE:
case WIRE_PEER_STORAGE_RETRIEVAL:
/* Ignore unknown messages (e.g. gossip). */
status_debug("Ignoring message type %d in simple close",
fromwire_peektype(msg));
break;
/* These are unexpected messages. Warn the peer */
case WIRE_INIT:
case WIRE_TX_ADD_INPUT:
case WIRE_TX_ADD_OUTPUT:
case WIRE_TX_REMOVE_INPUT:
case WIRE_TX_REMOVE_OUTPUT:
case WIRE_TX_COMPLETE:
case WIRE_TX_SIGNATURES:
case WIRE_TX_INIT_RBF:
case WIRE_TX_ACK_RBF:
case WIRE_TX_ABORT:
case WIRE_OPEN_CHANNEL:
case WIRE_ACCEPT_CHANNEL:
case WIRE_FUNDING_CREATED:
case WIRE_FUNDING_SIGNED:
case WIRE_CHANNEL_READY:
case WIRE_OPEN_CHANNEL2:
case WIRE_ACCEPT_CHANNEL2:
case WIRE_STFU:
case WIRE_SPLICE_INIT:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
case WIRE_SHUTDOWN:
case WIRE_UPDATE_ADD_HTLC:
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
case WIRE_UPDATE_BLOCKHEIGHT:
case WIRE_CHANNEL_REESTABLISH:
case WIRE_ANNOUNCEMENT_SIGNATURES:
peer_failed_warn(pps, &channel_id,
"Peer sent unexpected message %u (%s)",
fromwire_peektype(msg),
peer_wire_name(fromwire_peektype(msg)));
break;
}
}
/* Decide whether to ask master to delay broadcasting our closer tx.
* If our output (closer pays the full fee) is less than the peer's
* output AND our proposed fee is lower than theirs, their tx has a
* better chance of being mined first — give it an hour head-start.
* Skip when closer_amount is zero: our output is dust and will be
* omitted regardless, so there is nothing to protect by waiting. */
struct amount_sat closer_amount;
if (!amount_sat_sub(&closer_amount, local_sat, sent_fee))
closer_amount = AMOUNT_SAT(0);
bool delay_broadcast = amount_sat_greater(closer_amount, AMOUNT_SAT(0))
&& amount_sat_less(closer_amount, remote_sat)
&& amount_sat_less(sent_fee, their_fee);
if (delay_broadcast)
status_debug("Simple close: requesting delayed broadcast"
" (our output %s < peer %s, our fee %s < their fee %s)",
fmt_amount_sat(tmpctx, closer_amount),
fmt_amount_sat(tmpctx, remote_sat),
fmt_amount_sat(tmpctx, sent_fee),
fmt_amount_sat(tmpctx, their_fee));
wire_sync_write(REQ_FD, take(towire_simpleclosed_complete(NULL, delay_broadcast)));
tal_free(ctx);
daemon_shutdown();
}