diff --git a/closingd/simpleclosed.c b/closingd/simpleclosed.c index ab1e8dccc9cc..f504504b153d 100644 --- a/closingd/simpleclosed.c +++ b/closingd/simpleclosed.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -34,11 +35,6 @@ #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; @@ -57,13 +53,13 @@ static const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) 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) +static struct bitcoin_txid master_got_sig_ourtx(const struct bitcoin_tx *tx, + const struct bitcoin_signature *sig) { struct bitcoin_txid txid; u8 *msg; - msg = towire_simpleclosed_got_sig(tmpctx, tx, sig); + msg = towire_simpleclosed_our_closing_tx(tmpctx, tx, sig); if (!wire_sync_write(REQ_FD, take(msg))) status_failed(STATUS_FAIL_MASTER_IO, "Writing got_sig: %s", @@ -74,7 +70,7 @@ static struct bitcoin_txid master_got_sig(struct bitcoin_tx *tx, status_failed(STATUS_FAIL_MASTER_IO, "Reading got_sig_reply: %s", strerror(errno)); - if (!fromwire_simpleclosed_got_sig_reply(msg, &txid)) + if (!fromwire_simpleclosed_our_closing_tx_reply(msg, &txid)) status_failed(STATUS_FAIL_MASTER_IO, "Bad got_sig_reply: %s", tal_hex(tmpctx, msg)); @@ -462,8 +458,8 @@ static struct bitcoin_tx *handle_closing_complete( /* Tell master to validate, store, and handle broadcast. */ wire_sync_write(REQ_FD, - take(towire_simpleclosed_closee_broadcast(NULL, - chosen_tx, &their_sig))); + take(towire_simpleclosed_their_closing_tx(NULL, + chosen_tx, &their_sig))); return chosen_tx; } @@ -686,7 +682,7 @@ int main(int argc, char *argv[]) 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); + txid = master_got_sig_ourtx(closing_tx, &their_sig); status_debug("Closer tx stored by master: %s", fmt_bitcoin_txid(tmpctx, &txid)); got_our_sig = true; diff --git a/closingd/simpleclosed_wire.csv b/closingd/simpleclosed_wire.csv index d3108e3a9ba0..3aadc7a4f42e 100644 --- a/closingd/simpleclosed_wire.csv +++ b/closingd/simpleclosed_wire.csv @@ -31,19 +31,19 @@ msgdata,simpleclosed_init,opener,enum side, # simpleclosed tells master it got a valid closing_sig for our closing_complete; # master should broadcast this tx. -msgtype,simpleclosed_got_sig,3002 -msgdata,simpleclosed_got_sig,tx,bitcoin_tx, -msgdata,simpleclosed_got_sig,sig,bitcoin_signature, +msgtype,simpleclosed_our_closing_tx,3002 +msgdata,simpleclosed_our_closing_tx,tx,bitcoin_tx, +msgdata,simpleclosed_our_closing_tx,their_sig,bitcoin_signature, # Master replies with the txid (after storing/broadcasting). -msgtype,simpleclosed_got_sig_reply,3102 -msgdata,simpleclosed_got_sig_reply,closing_txid,bitcoin_txid, +msgtype,simpleclosed_our_closing_tx_reply,3102 +msgdata,simpleclosed_our_closing_tx_reply,closing_txid,bitcoin_txid, # simpleclosed tells master it signed the peer's closing tx (as the closee); # master validates and stores, drop_to_chain handles broadcast. -msgtype,simpleclosed_closee_broadcast,3003 -msgdata,simpleclosed_closee_broadcast,tx,bitcoin_tx, -msgdata,simpleclosed_closee_broadcast,sig,bitcoin_signature, +msgtype,simpleclosed_their_closing_tx,3003 +msgdata,simpleclosed_their_closing_tx,tx,bitcoin_tx, +msgdata,simpleclosed_their_closing_tx,their_sig,bitcoin_signature, # Negotiations complete, exiting. msgtype,simpleclosed_complete,3004 diff --git a/common/Makefile b/common/Makefile index d60606420807..4828c228ed12 100644 --- a/common/Makefile +++ b/common/Makefile @@ -125,7 +125,8 @@ COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) \ common/hsm_version.h \ common/htlc.h \ common/jsonrpc_errors.h \ - common/overflows.h + common/overflows.h \ + common/simple_close_weight.h COMMON_HEADERS_GEN := common/htlc_state_names_gen.h common/status_wiregen.h common/peer_status_wiregen.h common/scb_wiregen.h common/gossip_store_wiregen.h diff --git a/common/simple_close_weight.h b/common/simple_close_weight.h new file mode 100644 index 000000000000..2f96d701a960 --- /dev/null +++ b/common/simple_close_weight.h @@ -0,0 +1,12 @@ +#ifndef LIGHTNING_COMMON_SIMPLE_CLOSE_WEIGHT_H +#define LIGHTNING_COMMON_SIMPLE_CLOSE_WEIGHT_H +#include "config.h" +/* When lightningd checks the closing tx, it must use the same weight + * approximation as simpleclosed, otherwise it might reject it, so we share + * this constant. */ + +/* 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 +#endif /* LIGHTNING_COMMON_SIMPLE_CLOSE_WEIGHT_H */ diff --git a/common/test/run-htable.c b/common/test/run-htable.c index 32a2631c37af..267ad8c03f10 100644 --- a/common/test/run-htable.c +++ b/common/test/run-htable.c @@ -81,15 +81,6 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for memleak_scan_htable */ -void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/common/test/run-route-infloop.c b/common/test/run-route-infloop.c index 4e4ee302ff4a..7556fd2f93af 100644 --- a/common/test/run-route-infloop.c +++ b/common/test/run-route-infloop.c @@ -32,15 +32,6 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED, const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for memleak_scan_htable */ -void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for sciddir_or_pubkey_from_node_id */ bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, const struct node_id *node_id UNNEEDED) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index bfee7c084e6e..55aac6cab374 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -332,6 +332,29 @@ static struct bitcoin_tx *sign_and_send_last(const tal_t *ctx, return tx; } +/* Normally we only sign and broadcast our last_tx, but in the case of + * simple close, we want to broadcast theirs, but we don't bother + * saving it. We'll close the channel if/when we see it onchain. */ +void sign_and_broadcast_their_closing(struct channel *channel, + struct bitcoin_tx *tx, + const struct bitcoin_signature *their_sig) +{ + struct lightningd *ld = channel->peer->ld; + struct bitcoin_tx *signed_tx; + + /* We shouldn't get here, but in case we do. */ + if (channel->withheld) { + log_broken(channel->log, + "Withheld channel: should not have mutual close!"); + return; + } + + signed_tx = sign_last_tx(NULL, channel, tx, their_sig); + broadcast_tx(channel, ld->topology, channel, take(signed_tx), + cmd_id_from_close_command(tmpctx, ld, channel), + false, 0, NULL, NULL, NULL); +} + /* FIXME: reorder! */ static enum watch_result funding_spent(struct channel *channel, const struct bitcoin_tx *tx, diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index e5130ddd752b..def5e761f58f 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -129,6 +129,11 @@ void drop_to_chain(struct lightningd *ld, struct channel *channel, bool cooperative, const struct bitcoin_tx *unilateral_tx); +/* Special case of sending their mutual close */ +void sign_and_broadcast_their_closing(struct channel *channel, + struct bitcoin_tx *tx, + const struct bitcoin_signature *their_sig); + void update_channel_from_inflight(struct lightningd *ld, struct channel *channel, const struct channel_inflight *inflight, diff --git a/lightningd/simple_close_control.c b/lightningd/simple_close_control.c index 07e08473e630..d531b15cb5ca 100644 --- a/lightningd/simple_close_control.c +++ b/lightningd/simple_close_control.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -35,9 +36,13 @@ /* Check that tx spends exactly our funding outpoint and every output goes * to a known shutdown script. Returns an error string, or NULL on success. */ static const char *close_tx_check(const tal_t *ctx, - const struct channel *channel, - const struct bitcoin_tx *tx) + const struct channel *channel, + const struct bitcoin_tx *tx, + u32 max_feerate_wepay) { + bool our_output_exists = false, our_output_ok = false; + struct amount_sat expected_amt_tous; + if (tx->wtx->num_inputs != 1) return tal_fmt(ctx, "expected 1 input, got %zu", tx->wtx->num_inputs); @@ -46,6 +51,13 @@ static const char *close_tx_check(const tal_t *ctx, return tal_fmt(ctx, "does not spend funding outpoint %s", fmt_bitcoin_outpoint(ctx, &channel->funding)); + /* Lowest amount we expect: must match simpleclosed's heuristic */ + expected_amt_tous = amount_msat_to_sat_round_down(channel->our_msat); + if (!amount_sat_sub(&expected_amt_tous, + expected_amt_tous, + amount_sat((u64)max_feerate_wepay * SIMPLE_CLOSE_WEIGHT / 1000))) + expected_amt_tous = AMOUNT_SAT(0); + for (size_t i = 0; i < tx->wtx->num_outputs; i++) { const struct wally_tx_output *out = &tx->wtx->outputs[i]; /* Elements has an explicit fee output with no script. */ @@ -56,8 +68,15 @@ static const char *close_tx_check(const tal_t *ctx, } const u8 *script = tal_dup_arr(ctx, u8, out->script, out->script_len, 0); - if (scripteq(script, channel->shutdown_scriptpubkey[LOCAL])) + + /* In case they match our output, we check if *any* output to us is good */ + if (scripteq(script, channel->shutdown_scriptpubkey[LOCAL])) { + our_output_exists = true; + if (amount_sat_greater_eq(bitcoin_tx_output_get_amount_sat(tx, i), + expected_amt_tous)) + our_output_ok = true; continue; + } if (scripteq(script, channel->shutdown_scriptpubkey[REMOTE])) continue; /* Our own output is always paid to shutdown_scriptpubkey[LOCAL] @@ -87,12 +106,38 @@ static const char *close_tx_check(const tal_t *ctx, "output %zu goes to unknown script %s", i, tal_hex(ctx, script)); } + + /* If we expected an output, make sure it was ok. */ + if (amount_sat_greater_eq(expected_amt_tous, + channel->our_config.dust_limit)) { + if (!our_output_exists) + return tal_fmt(ctx, + "No output pays to us, and we expect at least %s", + fmt_amount_sat(tmpctx, expected_amt_tous)); + if (!our_output_ok) + return tal_fmt(ctx, + "Output to us doesn't pay enough (we expected at least %s)", + fmt_amount_sat(tmpctx, expected_amt_tous)); + } return NULL; } +static u32 feerate_for_close(const struct channel *channel) +{ + struct lightningd *ld = channel->peer->ld; + u32 feerate_perkw = mutual_close_feerate(ld->topology); + if (!feerate_perkw) { + feerate_perkw = get_feerate(channel->fee_states, + channel->opener, LOCAL) / 2; + if (feerate_perkw < get_feerate_floor(ld->topology)) + feerate_perkw = get_feerate_floor(ld->topology); + } + return feerate_perkw; +} + /* Master receives simpleclosed_got_sig: validate remote sig, store mutual * close tx, and reply with txid. drop_to_chain handles broadcast. */ -static void handle_simpleclosed_got_sig(struct channel *channel, const u8 *msg) +static void handle_simpleclosed_our_closing_tx(struct channel *channel, const u8 *msg) { struct lightningd *ld = channel->peer->ld; struct bitcoin_tx *tx; @@ -100,14 +145,15 @@ static void handle_simpleclosed_got_sig(struct channel *channel, const u8 *msg) struct bitcoin_signature sig; const u8 *funding_wscript; - if (!fromwire_simpleclosed_got_sig(tmpctx, msg, &tx, &sig)) { + if (!fromwire_simpleclosed_our_closing_tx(tmpctx, msg, &tx, &sig)) { channel_internal_error(channel, "bad simpleclosed_got_sig: %s", tal_hex(msg, msg)); return; } tx->chainparams = chainparams; - const char *err = close_tx_check(tmpctx, channel, tx); + const char *err = close_tx_check(tmpctx, channel, tx, + feerate_for_close(channel)); if (err) { channel_internal_error(channel, "bad simpleclosed_got_sig: %s", @@ -131,36 +177,36 @@ static void handle_simpleclosed_got_sig(struct channel *channel, const u8 *msg) bitcoin_txid(tx, &txid); log_info(channel->log, - "Simple close: stored closer tx %s", + "Simple close: stored our tx %s", fmt_bitcoin_txid(tmpctx, &txid)); subd_send_msg(channel->owner, - take(towire_simpleclosed_got_sig_reply(NULL, &txid))); + take(towire_simpleclosed_our_closing_tx_reply(NULL, &txid))); } -/* Master receives simpleclosed_closee_broadcast: validate remote sig and +/* Master receives simpleclosed_their_closing_tx: validate remote sig and * store the mutual close tx. drop_to_chain handles broadcast. */ -static void handle_simpleclosed_closee_broadcast(struct channel *channel, +static void handle_simpleclosed_their_closing_tx(struct channel *channel, const u8 *msg) { - struct lightningd *ld = channel->peer->ld; struct bitcoin_tx *tx; struct bitcoin_txid txid; struct bitcoin_signature sig; const u8 *funding_wscript; - if (!fromwire_simpleclosed_closee_broadcast(tmpctx, msg, &tx, &sig)) { + if (!fromwire_simpleclosed_their_closing_tx(tmpctx, msg, &tx, &sig)) { channel_internal_error(channel, - "bad simpleclosed_closee_broadcast: %s", + "bad simpleclosed_their_closing_tx: %s", tal_hex(msg, msg)); return; } tx->chainparams = chainparams; - const char *err = close_tx_check(tmpctx, channel, tx); + /* Their tx, we don't pay any fee */ + const char *err = close_tx_check(tmpctx, channel, tx, 0); if (err) { channel_internal_error(channel, - "bad simpleclosed_closee_broadcast: %s", + "bad simpleclosed_their_closing_tx: %s", err); return; } @@ -171,17 +217,17 @@ static void handle_simpleclosed_closee_broadcast(struct channel *channel, if (!check_tx_sig(tx, 0, NULL, funding_wscript, &channel->channel_info.remote_fundingkey, &sig)) { channel_internal_error(channel, - "bad simpleclosed_closee_broadcast: invalid sig: %s", + "bad simpleclosed_their_closing_tx: invalid sig: %s", tal_hex(msg, msg)); return; } - channel_set_last_tx(channel, tx, &sig); - wallet_channel_save(ld->wallet, channel); + /* We don't save their tx, we just broadcast it. */ + sign_and_broadcast_their_closing(channel, tx, &sig); bitcoin_txid(tx, &txid); log_info(channel->log, - "Simple close: stored closee tx %s", + "Simple close: broadcast their tx %s", fmt_bitcoin_txid(tmpctx, &txid)); } @@ -242,11 +288,11 @@ static unsigned int simpleclosed_msg(struct subd *sd, const u8 *msg, enum simpleclosed_wire t = fromwire_peektype(msg); switch (t) { - case WIRE_SIMPLECLOSED_GOT_SIG: - handle_simpleclosed_got_sig(sd->channel, msg); + case WIRE_SIMPLECLOSED_OUR_CLOSING_TX: + handle_simpleclosed_our_closing_tx(sd->channel, msg); return 0; - case WIRE_SIMPLECLOSED_CLOSEE_BROADCAST: - handle_simpleclosed_closee_broadcast(sd->channel, msg); + case WIRE_SIMPLECLOSED_THEIR_CLOSING_TX: + handle_simpleclosed_their_closing_tx(sd->channel, msg); return 0; case WIRE_SIMPLECLOSED_COMPLETE: handle_simpleclosed_complete(sd->channel, msg); @@ -254,7 +300,7 @@ static unsigned int simpleclosed_msg(struct subd *sd, const u8 *msg, /* Inbound-only (master→daemon) — should not be received here. */ case WIRE_SIMPLECLOSED_INIT: - case WIRE_SIMPLECLOSED_GOT_SIG_REPLY: + case WIRE_SIMPLECLOSED_OUR_CLOSING_TX_REPLY: break; } @@ -264,7 +310,6 @@ static unsigned int simpleclosed_msg(struct subd *sd, const u8 *msg, void peer_start_simpleclosed(struct channel *channel, struct peer_fd *peer_fd) { u8 *initmsg; - u32 feerate_perkw; struct amount_msat their_msat; int hsmfd; struct lightningd *ld = channel->peer->ld; @@ -318,14 +363,6 @@ void peer_start_simpleclosed(struct channel *channel, struct peer_fd *peer_fd) return; } - feerate_perkw = mutual_close_feerate(ld->topology); - if (!feerate_perkw) { - feerate_perkw = get_feerate(channel->fee_states, - channel->opener, LOCAL) / 2; - if (feerate_perkw < get_feerate_floor(ld->topology)) - feerate_perkw = get_feerate_floor(ld->topology); - } - /* Wallet key for our output. */ if (wallet_can_spend(ld->wallet, channel->shutdown_scriptpubkey[LOCAL], @@ -348,7 +385,7 @@ void peer_start_simpleclosed(struct channel *channel, struct peer_fd *peer_fd) &channel->channel_info.remote_fundingkey, amount_msat_to_sat_round_down(channel->our_msat), amount_msat_to_sat_round_down(their_msat), - channel->our_config.dust_limit, feerate_perkw, local_wallet_index, + channel->our_config.dust_limit, feerate_for_close(channel), local_wallet_index, local_wallet_ext_key, channel->shutdown_scriptpubkey[LOCAL], channel->shutdown_scriptpubkey[REMOTE], channel->opener); diff --git a/lightningd/subd.h b/lightningd/subd.h index 8518b065590a..3ad73bbba8f4 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -126,13 +126,13 @@ struct subd *new_channel_subd_(const tal_t *ctx, const char *(*msgname)(int msgtype), unsigned int (*msgcb)(struct subd *, const u8 *, const int *fds), - void (*errcb)(void *channel, + void (*errcb)(void *errcb_channel, struct peer_fd *peer_fd, const char *desc, const u8 *err_for_them, bool disconnect, bool warning), - void (*billboardcb)(void *channel, bool perm, + void (*billboardcb)(void *bollboardcb_channel, bool perm, const char *happenings), ...); diff --git a/lightningd/test/run-close_tx_check.c b/lightningd/test/run-close_tx_check.c index 9b132647bc64..59c5d91f6c26 100644 --- a/lightningd/test/run-close_tx_check.c +++ b/lightningd/test/run-close_tx_check.c @@ -55,15 +55,15 @@ void force_peer_disconnect(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, const char *why UNNEEDED) { fprintf(stderr, "force_peer_disconnect called!\n"); abort(); } -/* Generated stub for fromwire_simpleclosed_closee_broadcast */ -bool fromwire_simpleclosed_closee_broadcast(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct bitcoin_tx **tx UNNEEDED, struct bitcoin_signature *sig UNNEEDED) -{ fprintf(stderr, "fromwire_simpleclosed_closee_broadcast called!\n"); abort(); } /* Generated stub for fromwire_simpleclosed_complete */ bool fromwire_simpleclosed_complete(const void *p UNNEEDED, bool *delay_broadcast UNNEEDED) { fprintf(stderr, "fromwire_simpleclosed_complete called!\n"); abort(); } -/* Generated stub for fromwire_simpleclosed_got_sig */ -bool fromwire_simpleclosed_got_sig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct bitcoin_tx **tx UNNEEDED, struct bitcoin_signature *sig UNNEEDED) -{ fprintf(stderr, "fromwire_simpleclosed_got_sig called!\n"); abort(); } +/* Generated stub for fromwire_simpleclosed_our_closing_tx */ +bool fromwire_simpleclosed_our_closing_tx(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct bitcoin_tx **tx UNNEEDED, struct bitcoin_signature *their_sig UNNEEDED) +{ fprintf(stderr, "fromwire_simpleclosed_our_closing_tx called!\n"); abort(); } +/* Generated stub for fromwire_simpleclosed_their_closing_tx */ +bool fromwire_simpleclosed_their_closing_tx(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct bitcoin_tx **tx UNNEEDED, struct bitcoin_signature *their_sig UNNEEDED) +{ fprintf(stderr, "fromwire_simpleclosed_their_closing_tx called!\n"); abort(); } /* Generated stub for get_feerate_floor */ u32 get_feerate_floor(const struct chain_topology *topo UNNEEDED) { fprintf(stderr, "get_feerate_floor called!\n"); abort(); } @@ -100,7 +100,7 @@ struct subd *new_channel_subd_(const tal_t *ctx UNNEEDED, const u8 *err_for_them UNNEEDED, bool disconnect UNNEEDED, bool warning) UNNEEDED, - void (*billboardcb)(void *billboardcb_channel UNNEEDED, bool perm UNNEEDED, + void (*billboardcb)(void *bollboardcb_channel UNNEEDED, bool perm UNNEEDED, const char *happenings) UNNEEDED, ...) { fprintf(stderr, "new_channel_subd_ called!\n"); abort(); } @@ -108,18 +108,23 @@ struct subd *new_channel_subd_(const tal_t *ctx UNNEEDED, void resolve_close_command(struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, bool cooperative UNNEEDED, const struct bitcoin_tx **close_txs UNNEEDED) { fprintf(stderr, "resolve_close_command called!\n"); abort(); } +/* Generated stub for sign_and_broadcast_their_closing */ +void sign_and_broadcast_their_closing(struct channel *channel UNNEEDED, + struct bitcoin_tx *tx UNNEEDED, + const struct bitcoin_signature *their_sig UNNEEDED) +{ fprintf(stderr, "sign_and_broadcast_their_closing called!\n"); abort(); } /* Generated stub for simpleclosed_wire_name */ const char *simpleclosed_wire_name(int e UNNEEDED) { fprintf(stderr, "simpleclosed_wire_name called!\n"); abort(); } /* Generated stub for subd_send_msg */ void subd_send_msg(struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED) { fprintf(stderr, "subd_send_msg called!\n"); abort(); } -/* Generated stub for towire_simpleclosed_got_sig_reply */ -u8 *towire_simpleclosed_got_sig_reply(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *closing_txid UNNEEDED) -{ fprintf(stderr, "towire_simpleclosed_got_sig_reply called!\n"); abort(); } /* Generated stub for towire_simpleclosed_init */ u8 *towire_simpleclosed_init(const tal_t *ctx UNNEEDED, const struct chainparams *chainparams UNNEEDED, const struct channel_id *channel_id UNNEEDED, const struct bitcoin_outpoint *funding UNNEEDED, struct amount_sat funding_satoshi UNNEEDED, const struct pubkey *local_fundingkey UNNEEDED, const struct pubkey *remote_fundingkey UNNEEDED, struct amount_sat local_sat UNNEEDED, struct amount_sat remote_sat UNNEEDED, struct amount_sat our_dust_limit UNNEEDED, u32 feerate_perkw UNNEEDED, u32 *local_wallet_index UNNEEDED, const struct ext_key *local_wallet_ext_key UNNEEDED, const u8 *local_scriptpubkey UNNEEDED, const u8 *remote_scriptpubkey UNNEEDED, enum side opener UNNEEDED) { fprintf(stderr, "towire_simpleclosed_init called!\n"); abort(); } +/* Generated stub for towire_simpleclosed_our_closing_tx_reply */ +u8 *towire_simpleclosed_our_closing_tx_reply(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *closing_txid UNNEEDED) +{ fprintf(stderr, "towire_simpleclosed_our_closing_tx_reply called!\n"); abort(); } /* Generated stub for wallet_can_spend */ bool wallet_can_spend(struct wallet *w UNNEEDED, const u8 *script UNNEEDED, @@ -153,6 +158,7 @@ static const u8 op_return_burn[] = { /* Build a channel that only populates the fields close_tx_check reads. */ static struct channel *make_channel(const tal_t *ctx, const struct bitcoin_outpoint *funding, + struct amount_sat our_amt, const u8 *local_script, size_t local_len, const u8 *remote_script, size_t remote_len, bool simple_close_negotiated) @@ -180,17 +186,22 @@ static struct channel *make_channel(const tal_t *ctx, = tal_dup_arr(channel, u8, local_script, local_len, 0); channel->shutdown_scriptpubkey[REMOTE] = tal_dup_arr(channel, u8, remote_script, remote_len, 0); + if (!amount_sat_to_msat(&channel->our_msat, our_amt)) + abort(); return channel; } +/* closer gets 600k sat minus fee, closee gets 400ksat */ static struct bitcoin_tx *close_tx_with_scripts(const tal_t *ctx, const struct bitcoin_outpoint *funding, const u8 *closer_script, - const u8 *closee_script) + const u8 *closee_script, + u32 feerate) { struct pubkey pk1, pk2; const u8 *funding_wscript; struct bitcoin_tx *tx; + struct amount_sat to_closer; assert(pubkey_from_hexstr("034fede2c619f647fe7c01d40ae22e4c285291ca2ffb47937bbfb7d6e8285a081f", 2 * PUBKEY_CMPR_LEN, &pk1)); @@ -198,9 +209,13 @@ static struct bitcoin_tx *close_tx_with_scripts(const tal_t *ctx, 2 * PUBKEY_CMPR_LEN, &pk2)); funding_wscript = bitcoin_redeem_2of2(ctx, &pk1, &pk2); + to_closer = AMOUNT_SAT(600000); + if (!amount_sat_sub(&to_closer, to_closer, + amount_sat((u64)feerate * SIMPLE_CLOSE_WEIGHT / 1000))) + abort(); tx = create_simple_close_tx(ctx, NULL, NULL, closer_script, closee_script, funding_wscript, funding, AMOUNT_SAT(1000000), - AMOUNT_SAT(600000), AMOUNT_SAT(400000), 0); + to_closer, AMOUNT_SAT(400000), 0); assert(tx != NULL); tx->chainparams = chainparams; return tx; @@ -218,12 +233,12 @@ static void test_op_return_closer_accepted(void) const u8 *closee = tal_dup_arr(tmpctx, u8, p2wpkh_local, sizeof(p2wpkh_local), 0); memset(&funding, 0, sizeof(funding)); - channel = make_channel(tmpctx, &funding, + channel = make_channel(tmpctx, &funding, AMOUNT_SAT(400000), p2wpkh_local, sizeof(p2wpkh_local), p2wpkh_remote, sizeof(p2wpkh_remote), true); - tx = close_tx_with_scripts(tmpctx, &funding, closer, closee); + tx = close_tx_with_scripts(tmpctx, &funding, closer, closee, 0); - assert(close_tx_check(tmpctx, channel, tx) == NULL); + assert(close_tx_check(tmpctx, channel, tx, 0) == NULL); } /* Same tx, but option_simple_close was NOT negotiated: the OP_RETURN is not @@ -237,12 +252,12 @@ static void test_op_return_rejected_without_feature(void) const u8 *closee = tal_dup_arr(tmpctx, u8, p2wpkh_local, sizeof(p2wpkh_local), 0); memset(&funding, 0, sizeof(funding)); - channel = make_channel(tmpctx, &funding, + channel = make_channel(tmpctx, &funding, AMOUNT_SAT(600000), p2wpkh_local, sizeof(p2wpkh_local), p2wpkh_remote, sizeof(p2wpkh_remote), false); - tx = close_tx_with_scripts(tmpctx, &funding, closer, closee); + tx = close_tx_with_scripts(tmpctx, &funding, closer, closee, 0); - assert(close_tx_check(tmpctx, channel, tx) != NULL); + assert(close_tx_check(tmpctx, channel, tx, 0) != NULL); } /* A valid OP_RETURN script but with a non-zero value burns real funds; BOLT #2 @@ -256,10 +271,10 @@ static void test_op_return_nonzero_value_rejected(void) const u8 *closee = tal_dup_arr(tmpctx, u8, p2wpkh_local, sizeof(p2wpkh_local), 0); memset(&funding, 0, sizeof(funding)); - channel = make_channel(tmpctx, &funding, + channel = make_channel(tmpctx, &funding, AMOUNT_SAT(600000), p2wpkh_local, sizeof(p2wpkh_local), p2wpkh_remote, sizeof(p2wpkh_remote), true); - tx = close_tx_with_scripts(tmpctx, &funding, closer, closee); + tx = close_tx_with_scripts(tmpctx, &funding, closer, closee, 0); /* create_simple_close_tx forces the OP_RETURN value to zero; override it * to simulate a peer (or compromised subd) burning real funds. */ @@ -269,7 +284,7 @@ static void test_op_return_nonzero_value_rejected(void) tx->wtx->outputs[i].satoshi = 12345; } - assert(close_tx_check(tmpctx, channel, tx) != NULL); + assert(close_tx_check(tmpctx, channel, tx, 0) != NULL); } /* A non-OP_RETURN output that matches neither stored script is still an @@ -283,12 +298,12 @@ static void test_unknown_script_rejected(void) const u8 *closee = tal_dup_arr(tmpctx, u8, p2wpkh_local, sizeof(p2wpkh_local), 0); memset(&funding, 0, sizeof(funding)); - channel = make_channel(tmpctx, &funding, + channel = make_channel(tmpctx, &funding, AMOUNT_SAT(600000), p2wpkh_local, sizeof(p2wpkh_local), p2wpkh_remote, sizeof(p2wpkh_remote), true); - tx = close_tx_with_scripts(tmpctx, &funding, closer, closee); + tx = close_tx_with_scripts(tmpctx, &funding, closer, closee, 0); - assert(close_tx_check(tmpctx, channel, tx) != NULL); + assert(close_tx_check(tmpctx, channel, tx, 0) != NULL); } /* Ordinary close: both outputs go to the agreed shutdown scripts. */ @@ -301,12 +316,31 @@ static void test_known_scripts_accepted(void) const u8 *closee = tal_dup_arr(tmpctx, u8, p2wpkh_local, sizeof(p2wpkh_local), 0); memset(&funding, 0, sizeof(funding)); - channel = make_channel(tmpctx, &funding, + channel = make_channel(tmpctx, &funding, AMOUNT_SAT(400000), p2wpkh_local, sizeof(p2wpkh_local), p2wpkh_remote, sizeof(p2wpkh_remote), true); - tx = close_tx_with_scripts(tmpctx, &funding, closer, closee); + tx = close_tx_with_scripts(tmpctx, &funding, closer, closee, 0); - assert(close_tx_check(tmpctx, channel, tx) == NULL); + assert(close_tx_check(tmpctx, channel, tx, 0) == NULL); +} + +static void test_fee_too_high(void) +{ + struct bitcoin_outpoint funding; + struct channel *channel; + struct bitcoin_tx *tx; + const u8 *closee = tal_dup_arr(tmpctx, u8, p2wpkh_remote, sizeof(p2wpkh_remote), 0); + const u8 *closer = tal_dup_arr(tmpctx, u8, p2wpkh_local, sizeof(p2wpkh_local), 0); + + memset(&funding, 0, sizeof(funding)); + channel = make_channel(tmpctx, &funding, AMOUNT_SAT(600000), + p2wpkh_local, sizeof(p2wpkh_local), + p2wpkh_remote, sizeof(p2wpkh_remote), true); + tx = close_tx_with_scripts(tmpctx, &funding, closer, closee, 200); + + assert(close_tx_check(tmpctx, channel, tx, 0) != NULL); + assert(close_tx_check(tmpctx, channel, tx, 199) != NULL); + assert(close_tx_check(tmpctx, channel, tx, 200) == NULL); } int main(int argc, char *argv[]) @@ -319,6 +353,7 @@ int main(int argc, char *argv[]) test_op_return_nonzero_value_rejected(); test_unknown_script_rejected(); test_known_scripts_accepted(); + test_fee_too_high(); common_shutdown(); return 0; diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 0b42cb1c7178..d41bf5466e62 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -257,16 +257,6 @@ struct channel *find_channel_by_id(const struct peer *peer UNNEEDED, struct plugin *find_plugin_for_command(struct lightningd *ld UNNEEDED, const char *cmd_name UNNEEDED) { fprintf(stderr, "find_plugin_for_command called!\n"); abort(); } -/* Generated stub for find_txwatch_ */ -struct txwatch *find_txwatch_(struct chain_topology *topo UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, - enum watch_result (*cb)(struct lightningd *ld UNNEEDED, - const struct bitcoin_txid * UNNEEDED, - const struct bitcoin_tx * UNNEEDED, - unsigned int depth UNNEEDED, - void *arg) UNNEEDED, - void *arg UNNEEDED) -{ fprintf(stderr, "find_txwatch_ called!\n"); abort(); } /* Generated stub for fixup_htlcs_out */ void fixup_htlcs_out(struct lightningd *ld UNNEEDED) { fprintf(stderr, "fixup_htlcs_out called!\n"); abort(); } diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index a389a499038a..485490201537 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -88,15 +88,9 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx UNNEEDED, /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } /* Generated stub for memleak_ptr */ bool memleak_ptr(struct htable *memtable UNNEEDED, const void *p UNNEEDED) { fprintf(stderr, "memleak_ptr called!\n"); abort(); } -/* Generated stub for memleak_scan_htable */ -void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } /* Generated stub for memleak_scan_obj */ void memleak_scan_obj(struct htable *memtable UNNEEDED, const void *obj UNNEEDED) { fprintf(stderr, "memleak_scan_obj called!\n"); abort(); } diff --git a/tests/test_closing.py b/tests/test_closing.py index 97690f53447b..afbe45724a87 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -4372,7 +4372,7 @@ def test_simple_close_restart(node_factory, bitcoind): def test_simple_close_closee_path(node_factory, bitcoind): """Each node acts as both closer and closee simultaneously. Verify that handle_simpleclosed_closee_broadcast runs on both nodes (confirmed by the - 'stored closee tx' log) so the peer's closing tx is persisted. Both + 'stored closee tx' log), and that the peer's own closing tx is persisted. Both nodes must claim their output from whichever tx wins the race.""" opts = {'experimental-simple-close': None} l1, l2 = node_factory.line_graph(2, opts=opts) @@ -4389,9 +4389,9 @@ def test_simple_close_closee_path(node_factory, bitcoind): # is received before closing_sig in the protocol), so use wait_for_logs to # find both in any order rather than two sequential wait_for_log calls. l1.daemon.wait_for_logs(['Simple close: stored closer tx', - 'Simple close: stored closee tx']) + 'Simple close: broadcast closee tx']) l2.daemon.wait_for_logs(['Simple close: stored closer tx', - 'Simple close: stored closee tx']) + 'Simple close: broadcast closee tx']) # One of the two conflicting txs confirms; both nodes must see their output. bitcoind.generate_block(1, wait_for_mempool=1) @@ -4402,6 +4402,34 @@ def test_simple_close_closee_path(node_factory, bitcoind): wait_for(lambda: confirmed_txid in {o['txid'] for o in l2.rpc.listfunds()['outputs']}) +def test_simple_close_closee_only(node_factory, bitcoind, executor): + """l1 doesn't get the signature for its own close, but DOES NOT persist the peer's tx.""" + # l2 has low fees, so txs are different. + opts = [{'experimental-simple-close': None}, + {'experimental-simple-close': None, + 'feerates': (1010, 1010, 1010, 1010, 1010), + 'disconnect': ['-WIRE_CLOSING_SIG']}] + l1, l2 = node_factory.line_graph(2, opts=opts) + + l1.pay(l2, 200000000) + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['htlcs'] == []) + + # Get last tx now. + last_tx = only_one(l1.db_query("SELECT last_tx FROM channels;"))['last_tx'] + + # This won't exit yet. + executor.submit(l1.rpc.close, l2.info['id']) + l2.daemon.wait_for_log('dev_disconnect: -WIRE_CLOSING_SIG') + + l1.daemon.wait_for_log('Simple close: broadcast their tx') + time.sleep(1) + assert not l1.daemon.is_in_log('Simple close: stored our tx') + + # We do NOT update last_tx to their tx. + l1.stop() + assert only_one(l1.db_query("SELECT last_tx FROM channels;"))['last_tx'] == last_tx + + def test_simple_close_delay_broadcast(node_factory, bitcoind, executor): """When the closer has less output AND proposes a lower fee than the peer, it must log a 1-hour delay and let the peer's higher-fee tx get mined first. diff --git a/wallet/test/run-chain_moves_duplicate-detect.c b/wallet/test/run-chain_moves_duplicate-detect.c index cc2797950bd3..0bfb150682de 100644 --- a/wallet/test/run-chain_moves_duplicate-detect.c +++ b/wallet/test/run-chain_moves_duplicate-detect.c @@ -377,11 +377,6 @@ const char *wait_index_name(enum wait_index index UNNEEDED) /* Generated stub for wait_subsystem_name */ const char *wait_subsystem_name(enum wait_subsystem subsystem UNNEEDED) { fprintf(stderr, "wait_subsystem_name called!\n"); abort(); } -/* Generated stub for watchman_unwatch_outpoint */ -void watchman_unwatch_outpoint(struct lightningd *ld UNNEEDED, - const char *owner UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED) -{ fprintf(stderr, "watchman_unwatch_outpoint called!\n"); abort(); } /* Generated stub for watchman_watch_outpoint */ void watchman_watch_outpoint(struct lightningd *ld UNNEEDED, const char *owner UNNEEDED, diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 801839c678bb..8494e7df11ea 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -390,11 +390,6 @@ const char *wait_index_name(enum wait_index index UNNEEDED) /* Generated stub for wait_subsystem_name */ const char *wait_subsystem_name(enum wait_subsystem subsystem UNNEEDED) { fprintf(stderr, "wait_subsystem_name called!\n"); abort(); } -/* Generated stub for watchman_unwatch_outpoint */ -void watchman_unwatch_outpoint(struct lightningd *ld UNNEEDED, - const char *owner UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED) -{ fprintf(stderr, "watchman_unwatch_outpoint called!\n"); abort(); } /* Generated stub for watchman_watch_outpoint */ void watchman_watch_outpoint(struct lightningd *ld UNNEEDED, const char *owner UNNEEDED, diff --git a/wallet/test/run-migrate_remove_chain_moves_duplicates.c b/wallet/test/run-migrate_remove_chain_moves_duplicates.c index ad0550a6be5f..d9478ad8e310 100644 --- a/wallet/test/run-migrate_remove_chain_moves_duplicates.c +++ b/wallet/test/run-migrate_remove_chain_moves_duplicates.c @@ -426,11 +426,6 @@ const char *wait_index_name(enum wait_index index UNNEEDED) /* Generated stub for wait_subsystem_name */ const char *wait_subsystem_name(enum wait_subsystem subsystem UNNEEDED) { fprintf(stderr, "wait_subsystem_name called!\n"); abort(); } -/* Generated stub for watchman_unwatch_outpoint */ -void watchman_unwatch_outpoint(struct lightningd *ld UNNEEDED, - const char *owner UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED) -{ fprintf(stderr, "watchman_unwatch_outpoint called!\n"); abort(); } /* Generated stub for watchman_watch_outpoint */ void watchman_watch_outpoint(struct lightningd *ld UNNEEDED, const char *owner UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index c44307710943..0098e96af566 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -256,16 +256,6 @@ void fatal(const char *fmt UNNEEDED, ...) /* Generated stub for fatal_vfmt */ void fatal_vfmt(const char *fmt UNNEEDED, va_list ap UNNEEDED) { fprintf(stderr, "fatal_vfmt called!\n"); abort(); } -/* Generated stub for find_txwatch_ */ -struct txwatch *find_txwatch_(struct chain_topology *topo UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, - enum watch_result (*cb)(struct lightningd *ld UNNEEDED, - const struct bitcoin_txid * UNNEEDED, - const struct bitcoin_tx * UNNEEDED, - unsigned int depth UNNEEDED, - void *arg) UNNEEDED, - void *arg UNNEEDED) -{ fprintf(stderr, "find_txwatch_ called!\n"); abort(); } /* Generated stub for force_peer_disconnect */ void force_peer_disconnect(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, @@ -817,11 +807,6 @@ struct txowatch *watch_txo(const tal_t *ctx UNNEEDED, size_t input_num UNNEEDED, const struct block *block)) { fprintf(stderr, "watch_txo called!\n"); abort(); } -/* Generated stub for watchman_unwatch_outpoint */ -void watchman_unwatch_outpoint(struct lightningd *ld UNNEEDED, - const char *owner UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED) -{ fprintf(stderr, "watchman_unwatch_outpoint called!\n"); abort(); } /* Generated stub for watchman_watch_outpoint */ void watchman_watch_outpoint(struct lightningd *ld UNNEEDED, const char *owner UNNEEDED,