From fc54c197166f1eb404b61cf43683905f603491ec Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 23 Mar 2023 16:15:52 +1030 Subject: [PATCH] lightningd: provide callback in broadcast_tx() for refreshing tx. We'll use this to do RBF. Signed-off-by: Rusty Russell --- lightningd/chaintopology.c | 29 +++++++++++++++---- lightningd/chaintopology.h | 31 ++++++++++++++++----- lightningd/onchain_control.c | 3 +- lightningd/peer_control.c | 3 +- lightningd/test/run-invoice-select-inchan.c | 19 +++++++------ wallet/test/run-wallet.c | 19 +++++++------ 6 files changed, 73 insertions(+), 31 deletions(-) diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 9b7817b97..d1035ee5c 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -155,6 +155,7 @@ static void rebroadcast_txs(struct chain_topology *topo) struct txs_to_broadcast *txs; struct outgoing_tx *otx; struct outgoing_tx_map_iter it; + tal_t *cleanup_ctx = tal(NULL, char); txs = tal(topo, struct txs_to_broadcast); txs->cmd_id = tal_arr(txs, const char *, 0); @@ -167,10 +168,18 @@ static void rebroadcast_txs(struct chain_topology *topo) if (wallet_transaction_height(topo->ld->wallet, &otx->txid)) continue; + /* Don't free from txmap inside loop! */ + if (otx->refresh + && !otx->refresh(otx->channel, &otx->tx, otx->refresh_arg)) { + tal_steal(cleanup_ctx, otx); + continue; + } + tal_arr_expand(&txs->txs, fmt_bitcoin_tx(txs->txs, otx->tx)); tal_arr_expand(&txs->cmd_id, otx->cmd_id ? tal_strdup(txs, otx->cmd_id) : NULL); } + tal_free(cleanup_ctx); /* Let this do the dirty work. */ txs->cursor = (size_t)-1; @@ -220,12 +229,16 @@ static void broadcast_done(struct bitcoind *bitcoind, } } -void broadcast_tx(struct chain_topology *topo, - struct channel *channel, const struct bitcoin_tx *tx, - const char *cmd_id, bool allowhighfees, - void (*finished)(struct channel *channel, - bool success, - const char *err)) +void broadcast_tx_(struct chain_topology *topo, + struct channel *channel, const struct bitcoin_tx *tx, + const char *cmd_id, bool allowhighfees, + void (*finished)(struct channel *channel, + bool success, + const char *err), + bool (*refresh)(struct channel *channel, + const struct bitcoin_tx **tx, + void *arg), + void *refresh_arg) { /* Channel might vanish: topo owns it to start with. */ struct outgoing_tx *otx = tal(topo, struct outgoing_tx); @@ -234,6 +247,10 @@ void broadcast_tx(struct chain_topology *topo, bitcoin_txid(tx, &otx->txid); otx->tx = clone_bitcoin_tx(otx, tx); otx->finished = finished; + otx->refresh = refresh; + otx->refresh_arg = refresh_arg; + if (taken(otx->refresh_arg)) + tal_steal(otx, otx->refresh_arg); if (cmd_id) otx->cmd_id = tal_strdup(otx, cmd_id); else diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index 87f0a285e..fa3b1f56a 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -23,6 +23,8 @@ struct outgoing_tx { struct bitcoin_txid txid; const char *cmd_id; void (*finished)(struct channel *channel, bool success, const char *err); + bool (*refresh)(struct channel *, const struct bitcoin_tx **, void *arg); + void *refresh_arg; }; struct block { @@ -179,14 +181,29 @@ u32 penalty_feerate(struct chain_topology *topo); * @cmd_id: the JSON command id which triggered this (or NULL). * @allowhighfees: set to true to override the high-fee checks in the backend. * @finished: if non-NULL, call that and don't rebroadcast. + * @refresh: if non-NULL, callback before re-broadcasting (can replace tx): + * if returns false, delete. + * @refresh_arg: argument for @refresh */ -void broadcast_tx(struct chain_topology *topo, - struct channel *channel, - const struct bitcoin_tx *tx TAKES, - const char *cmd_id, bool allowhighfees, - void (*finished)(struct channel *, - bool success, - const char *err)); +#define broadcast_tx(topo, channel, tx, cmd_id, allowhighfees, \ + finished, refresh, refresh_arg) \ + broadcast_tx_((topo), (channel), (tx), (cmd_id), (allowhighfees), \ + (finished), \ + typesafe_cb_preargs(bool, void *, \ + (refresh), (refresh_arg), \ + struct channel *, \ + const struct bitcoin_tx **), \ + (refresh_arg)) + +void broadcast_tx_(struct chain_topology *topo, + struct channel *channel, + const struct bitcoin_tx *tx TAKES, + const char *cmd_id, bool allowhighfees, + void (*finished)(struct channel *, + bool success, + const char *err), + bool (*refresh)(struct channel *, const struct bitcoin_tx **, void *), + void *refresh_arg TAKES); struct chain_topology *new_topology(struct lightningd *ld, struct log *log); void setup_topology(struct chain_topology *topology, diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index c243b4735..a472e7355 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -346,7 +346,8 @@ static void handle_onchain_broadcast_tx(struct channel *channel, * high feerates as protection against the MAD-HTLC attack. */ broadcast_tx(channel->peer->ld->topology, channel, tx, NULL, is_rbf, - is_rbf ? &handle_onchain_broadcast_rbf_tx_cb : NULL); + is_rbf ? &handle_onchain_broadcast_rbf_tx_cb : NULL, + NULL, NULL); } static void handle_onchain_unwatch_tx(struct channel *channel, const u8 *msg) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 3f8ac6852..d281a6059 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -284,7 +284,8 @@ static void sign_and_send_last(struct lightningd *ld, /* Keep broadcasting until we say stop (can fail due to dup, * if they beat us to the broadcast). */ - broadcast_tx(ld->topology, channel, last_tx, cmd_id, false, NULL); + broadcast_tx(ld->topology, channel, last_tx, cmd_id, false, NULL, + NULL, NULL); remove_sig(last_tx); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index a99daaf72..09ca2b3dd 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -50,14 +50,17 @@ char *bolt11_encode_(const tal_t *ctx UNNEEDED, void *arg) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "bolt11_encode_ called!\n"); abort(); } -/* Generated stub for broadcast_tx */ -void broadcast_tx(struct chain_topology *topo UNNEEDED, - struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, - const char *cmd_id UNNEEDED, bool allowhighfees UNNEEDED, - void (*finished)(struct channel * UNNEEDED, - bool success UNNEEDED, - const char *err)) -{ fprintf(stderr, "broadcast_tx called!\n"); abort(); } +/* Generated stub for broadcast_tx_ */ +void broadcast_tx_(struct chain_topology *topo UNNEEDED, + struct channel *channel UNNEEDED, + const struct bitcoin_tx *tx TAKES UNNEEDED, + const char *cmd_id UNNEEDED, bool allowhighfees UNNEEDED, + void (*finished)(struct channel * UNNEEDED, + bool success UNNEEDED, + const char *err) UNNEEDED, + bool (*refresh)(struct channel * UNNEEDED, const struct bitcoin_tx ** UNNEEDED, void *) UNNEEDED, + void *refresh_arg TAKES UNNEEDED) +{ fprintf(stderr, "broadcast_tx_ called!\n"); abort(); } /* Generated stub for channel_change_state_reason_str */ const char *channel_change_state_reason_str(enum state_change reason UNNEEDED) { fprintf(stderr, "channel_change_state_reason_str called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a8d5b930b..d01088aa1 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -68,14 +68,17 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } -/* Generated stub for broadcast_tx */ -void broadcast_tx(struct chain_topology *topo UNNEEDED, - struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, - const char *cmd_id UNNEEDED, bool allowhighfees UNNEEDED, - void (*finished)(struct channel * UNNEEDED, - bool success UNNEEDED, - const char *err)) -{ fprintf(stderr, "broadcast_tx called!\n"); abort(); } +/* Generated stub for broadcast_tx_ */ +void broadcast_tx_(struct chain_topology *topo UNNEEDED, + struct channel *channel UNNEEDED, + const struct bitcoin_tx *tx TAKES UNNEEDED, + const char *cmd_id UNNEEDED, bool allowhighfees UNNEEDED, + void (*finished)(struct channel * UNNEEDED, + bool success UNNEEDED, + const char *err) UNNEEDED, + bool (*refresh)(struct channel * UNNEEDED, const struct bitcoin_tx ** UNNEEDED, void *) UNNEEDED, + void *refresh_arg TAKES UNNEEDED) +{ fprintf(stderr, "broadcast_tx_ called!\n"); abort(); } /* Generated stub for channel_tell_depth */ bool channel_tell_depth(struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED,