diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index fa884fafc..3db4106a8 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1931,6 +1931,9 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_GOT_OFFER: accepter_got_offer(dualopend, uc, msg); return 0; + case WIRE_DUALOPEND_GOT_RBF_OFFER: + // FIXME: do this + return 0; case WIRE_DUALOPEND_PSBT_CHANGED: if (uc->fc) { if (!uc->fc->cmd) { @@ -1998,6 +2001,7 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_REINIT: case WIRE_DUALOPEND_OPENER_INIT: case WIRE_DUALOPEND_GOT_OFFER_REPLY: + case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY: case WIRE_DUALOPEND_FAIL: case WIRE_DUALOPEND_PSBT_UPDATED: case WIRE_DUALOPEND_SEND_TX_SIGS: diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 7142a8b23..9c272933e 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -332,10 +332,7 @@ static void negotiation_failed(struct state *state, open_error(state, "You gave bad parameters: %s", errmsg); } -/* FIXME: remove this once used */ -void rbf_failed(struct state *state, const char *fmt, ...); -void rbf_failed(struct state *state, - const char *fmt, ...) +static void rbf_failed(struct state *state, const char *fmt, ...) { va_list ap; const char *errmsg; @@ -2351,6 +2348,180 @@ static void opener_start(struct state *state, u8 *msg) wire_sync_write(REQ_FD, take(msg)); } +static bool update_feerate(struct tx_state *tx_state, + u32 feerate_funding, + u32 last_feerate, + u8 fee_step) +{ + u32 feerate = feerate_funding; + + /* + * BOLT-487dd4b46aad9b59f7f3480b7bdf15862b52d2f9b #2: + * Each `fee_step` adds 1/4 (rounded down) to the initial + * transaction feerate, i.e. if the initial `feerate_per_kw_funding` + * was 512 satoshis per kiloweight, `fee_step` 1 is + * 512 + 512 / 4 or 640 sat/kw, `fee_step` 2 + * is 640 + 640 / 4 or 800 sat/kw. + */ + for (; fee_step > 0; fee_step--) + feerate += feerate / 4; + + /* It's possible they sent us a 'bad' feerate step, + * i.e. less than the last one. */ + if (feerate <= last_feerate) + return false; + + tx_state->feerate_per_kw_funding = feerate; + return true; +} + +static void rbf_start(struct state *state, const u8 *rbf_msg) +{ + struct channel_id cid; + struct tx_state *tx_state; + char *err_reason; + struct amount_sat total; + enum dualopend_wire msg_type; + u8 fee_step, *msg; + + /* We need a new tx_state! */ + tx_state = new_tx_state(state); + + if (!fromwire_init_rbf(rbf_msg, &cid, + &tx_state->opener_funding, + &tx_state->tx_locktime, + &fee_step)) + peer_failed_err(state->pps, &state->channel_id, + "Parsing init_rbf %s", + tal_hex(tmpctx, rbf_msg)); + + /* Is this the correct channel? */ + check_channel_id(state, &cid, &state->channel_id); + peer_billboard(false, "channel rbf: init received from peer"); + + /* Have you sent us everything we need yet ? */ + if (!state->tx_state->remote_funding_sigs_rcvd) + rbf_failed(state, "Last funding attempt not complete:" + " missing your funding tx_sigs"); + + /* FIXME: should we check for currently in progress? */ + + /* Copy over the channel config info -- everything except + * the reserve will be the same */ + tx_state->localconf = state->tx_state->localconf; + tx_state->remoteconf = state->tx_state->remoteconf; + + if (!update_feerate(tx_state, + state->feerate_per_kw_funding, + state->tx_state->feerate_per_kw_funding, + fee_step)) { + rbf_failed(state, "Fee step not greater than last." + " Fee step %d, last feerate %d", + fee_step, + state->tx_state->feerate_per_kw_funding); + return; + } + + /* We ask master if this is ok */ + msg = towire_dualopend_got_rbf_offer(NULL, + &state->channel_id, + tx_state->opener_funding, + tx_state->feerate_per_kw_funding, + tx_state->tx_locktime); + + wire_sync_write(REQ_FD, take(msg)); + msg = wire_sync_read(tmpctx, REQ_FD); + + if ((msg_type = fromwire_peektype(msg)) == WIRE_DUALOPEND_FAIL) { + if (!fromwire_dualopend_fail(msg, msg, &err_reason)) + master_badmsg(msg_type, msg); + rbf_failed(state, "%s", err_reason); + tal_free(tx_state); + return; + } + + if (!fromwire_dualopend_got_rbf_offer_reply(state, msg, + &tx_state->accepter_funding, + &tx_state->psbt)) + master_badmsg(WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY, msg); + + if (!tx_state->psbt) + tx_state->psbt = create_psbt(tx_state, 0, 0, + tx_state->tx_locktime); + + /* Check that total funding doesn't overflow */ + if (!amount_sat_add(&total, tx_state->opener_funding, + tx_state->accepter_funding)) { + rbf_failed(state, "Amount overflow. Local sats %s. " + "Remote sats %s", + type_to_string(tmpctx, struct amount_sat, + &tx_state->accepter_funding), + type_to_string(tmpctx, struct amount_sat, + &tx_state->opener_funding)); + tal_free(tx_state); + return; + } + + /* Check that total funding doesn't exceed allowed channel capacity */ + /* BOLT #2: + * + * The receiving node MUST fail the channel if: + *... + * - `funding_satoshis` is greater than or equal to 2^24 and the receiver does not support + * `option_support_large_channel`. */ + /* We choose to require *negotiation*, not just support! */ + if (!feature_negotiated(state->our_features, state->their_features, + OPT_LARGE_CHANNELS) + && amount_sat_greater(total, chainparams->max_funding)) { + rbf_failed(state, "total funding_satoshis %s too large", + type_to_string(tmpctx, struct amount_sat, &total)); + tal_free(tx_state); + return; + } + + /* Add all of our inputs/outputs to the changeset */ + init_changeset(tx_state, tx_state->psbt); + + /* Now that we know the total of the channel, we can set the reserve */ + set_reserve(tx_state, total); + + if (!check_config_bounds(tmpctx, total, + state->feerate_per_kw_commitment, + state->max_to_self_delay, + state->min_effective_htlc_capacity, + &tx_state->remoteconf, + &tx_state->localconf, + false, + true, /* v2 means we use anchor outputs */ + &err_reason)) { + rbf_failed(state, "%s", err_reason); + tal_free(tx_state); + return; + } + + msg = towire_ack_rbf(tmpctx, &state->channel_id, + state->our_role == TX_INITIATOR ? + tx_state->opener_funding : + tx_state->accepter_funding); + sync_crypto_write(state->pps, msg); + peer_billboard(false, "channel rbf: ack sent, waiting for reply"); + + + /* This is unused in this flow. We re-use + * the wire method between accepter + opener, so we set it + * to an invalid number, 1 (initiator sets; valid is even) */ + tx_state->funding_serial = 1; + /* Now we figure out what the proposed new open transaction is */ + if (!run_tx_interactive(state, tx_state, + &tx_state->psbt, TX_ACCEPTER)) + return; + + /* Find the funding transaction txid */ + psbt_txid(NULL, tx_state->psbt, &tx_state->funding_txid, NULL); + + // FIXME: same as accepter run now? +} + static u8 *handle_funding_locked(struct state *state, u8 *msg) { struct channel_id cid; @@ -2697,9 +2868,11 @@ static u8 *handle_master_in(struct state *state) case WIRE_DUALOPEND_FAIL: case WIRE_DUALOPEND_PSBT_UPDATED: case WIRE_DUALOPEND_GOT_OFFER_REPLY: + case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY: /* Messages we send */ case WIRE_DUALOPEND_GOT_OFFER: + case WIRE_DUALOPEND_GOT_RBF_OFFER: case WIRE_DUALOPEND_PSBT_CHANGED: case WIRE_DUALOPEND_COMMIT_RCVD: case WIRE_DUALOPEND_FUNDING_SIGS: @@ -2759,7 +2932,7 @@ static u8 *handle_peer_in(struct state *state) handle_peer_shutdown(state, msg); return NULL; case WIRE_INIT_RBF: - // FIXME: rbf_start? + rbf_start(state, msg); return NULL; /* Otherwise we fall through */ case WIRE_INIT: diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 6dd8e75e6..cae3c253e 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -98,6 +98,18 @@ msgdata,dualopend_got_offer_reply,psbt,wally_psbt, msgdata,dualopend_got_offer_reply,shutdown_len,u16, msgdata,dualopend_got_offer_reply,our_shutdown_scriptpubkey,?u8,shutdown_len +# dualopend->master: they offered a RBF, should we continue? +msgtype,dualopend_got_rbf_offer,7500 +msgdata,dualopend_got_rbf_offer,channel_id,channel_id, +msgdata,dualopend_got_rbf_offer,opener_funding,amount_sat, +msgdata,dualopend_got_rbf_offer,funding_feerate_per_kw,u32, +msgdata,dualopend_got_rbf_offer,locktime,u32, + +# master->dualopend: reply back with our funding info/contribs +msgtype,dualopend_got_rbf_offer_reply,7505 +msgdata,dualopend_got_rbf_offer_reply,accepter_funding,amount_sat, +msgdata,dualopend_got_rbf_offer_reply,psbt,wally_psbt, + # dualopend->master: ready to commit channel open to database and # get some signatures for the funding_tx. msgtype,dualopend_commit_rcvd,7007 diff --git a/openingd/dualopend_wiregen.c b/openingd/dualopend_wiregen.c index 92dd2881d..5af6ac575 100644 --- a/openingd/dualopend_wiregen.c +++ b/openingd/dualopend_wiregen.c @@ -24,6 +24,8 @@ const char *dualopend_wire_name(int e) case WIRE_DUALOPEND_REINIT: return "WIRE_DUALOPEND_REINIT"; case WIRE_DUALOPEND_GOT_OFFER: return "WIRE_DUALOPEND_GOT_OFFER"; case WIRE_DUALOPEND_GOT_OFFER_REPLY: return "WIRE_DUALOPEND_GOT_OFFER_REPLY"; + case WIRE_DUALOPEND_GOT_RBF_OFFER: return "WIRE_DUALOPEND_GOT_RBF_OFFER"; + case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY: return "WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY"; case WIRE_DUALOPEND_COMMIT_RCVD: return "WIRE_DUALOPEND_COMMIT_RCVD"; case WIRE_DUALOPEND_PSBT_CHANGED: return "WIRE_DUALOPEND_PSBT_CHANGED"; case WIRE_DUALOPEND_PSBT_UPDATED: return "WIRE_DUALOPEND_PSBT_UPDATED"; @@ -56,6 +58,8 @@ bool dualopend_wire_is_defined(u16 type) case WIRE_DUALOPEND_REINIT:; case WIRE_DUALOPEND_GOT_OFFER:; case WIRE_DUALOPEND_GOT_OFFER_REPLY:; + case WIRE_DUALOPEND_GOT_RBF_OFFER:; + case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY:; case WIRE_DUALOPEND_COMMIT_RCVD:; case WIRE_DUALOPEND_PSBT_CHANGED:; case WIRE_DUALOPEND_PSBT_UPDATED:; @@ -353,6 +357,58 @@ bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct return cursor != NULL; } +/* WIRE: DUALOPEND_GOT_RBF_OFFER */ +/* dualopend->master: they offered a RBF */ +u8 *towire_dualopend_got_rbf_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, u32 funding_feerate_per_kw, u32 locktime) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_DUALOPEND_GOT_RBF_OFFER); + towire_channel_id(&p, channel_id); + towire_amount_sat(&p, opener_funding); + towire_u32(&p, funding_feerate_per_kw); + towire_u32(&p, locktime); + + return memcheck(p, tal_count(p)); +} +bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, u32 *funding_feerate_per_kw, u32 *locktime) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_GOT_RBF_OFFER) + return false; + fromwire_channel_id(&cursor, &plen, channel_id); + *opener_funding = fromwire_amount_sat(&cursor, &plen); + *funding_feerate_per_kw = fromwire_u32(&cursor, &plen); + *locktime = fromwire_u32(&cursor, &plen); + return cursor != NULL; +} + +/* WIRE: DUALOPEND_GOT_RBF_OFFER_REPLY */ +/* master->dualopend: reply back with our funding info/contribs */ +u8 *towire_dualopend_got_rbf_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY); + towire_amount_sat(&p, accepter_funding); + towire_wally_psbt(&p, psbt); + + return memcheck(p, tal_count(p)); +} +bool fromwire_dualopend_got_rbf_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY) + return false; + *accepter_funding = fromwire_amount_sat(&cursor, &plen); + *psbt = fromwire_wally_psbt(ctx, &cursor, &plen); + return cursor != NULL; +} + /* WIRE: DUALOPEND_COMMIT_RCVD */ /* dualopend->master: ready to commit channel open to database and */ /* get some signatures for the funding_tx. */ @@ -862,4 +918,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:6e149f437eae7fde2f891bbb7f36903fa105179d9a97cd1b765d34641c0839ce +// SHA256STAMP:d9a02c4575a71748388be74b56f3fe333147557aa715e6735b9a1c35a72c7cd5 diff --git a/openingd/dualopend_wiregen.h b/openingd/dualopend_wiregen.h index 1c6443cae..d6473b44e 100644 --- a/openingd/dualopend_wiregen.h +++ b/openingd/dualopend_wiregen.h @@ -27,6 +27,10 @@ enum dualopend_wire { WIRE_DUALOPEND_GOT_OFFER = 7005, /* master->dualopend: reply back with our first funding info/contribs */ WIRE_DUALOPEND_GOT_OFFER_REPLY = 7105, + /* dualopend->master: they offered a RBF */ + WIRE_DUALOPEND_GOT_RBF_OFFER = 7500, + /* master->dualopend: reply back with our funding info/contribs */ + WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY = 7505, /* dualopend->master: ready to commit channel open to database and */ /* get some signatures for the funding_tx. */ WIRE_DUALOPEND_COMMIT_RCVD = 7007, @@ -98,6 +102,16 @@ bool fromwire_dualopend_got_offer(const tal_t *ctx, const void *p, struct channe u8 *towire_dualopend_got_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, u32 feerate_funding, const struct wally_psbt *psbt, const u8 *our_shutdown_scriptpubkey); bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, u32 *feerate_funding, struct wally_psbt **psbt, u8 **our_shutdown_scriptpubkey); +/* WIRE: DUALOPEND_GOT_RBF_OFFER */ +/* dualopend->master: they offered a RBF */ +u8 *towire_dualopend_got_rbf_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, u32 funding_feerate_per_kw, u32 locktime); +bool fromwire_dualopend_got_rbf_offer(const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, u32 *funding_feerate_per_kw, u32 *locktime); + +/* WIRE: DUALOPEND_GOT_RBF_OFFER_REPLY */ +/* master->dualopend: reply back with our funding info/contribs */ +u8 *towire_dualopend_got_rbf_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt); +bool fromwire_dualopend_got_rbf_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt); + /* WIRE: DUALOPEND_COMMIT_RCVD */ /* dualopend->master: ready to commit channel open to database and */ /* get some signatures for the funding_tx. */ @@ -195,4 +209,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_OPENINGD_DUALOPEND_WIREGEN_H */ -// SHA256STAMP:6e149f437eae7fde2f891bbb7f36903fa105179d9a97cd1b765d34641c0839ce +// SHA256STAMP:d9a02c4575a71748388be74b56f3fe333147557aa715e6735b9a1c35a72c7cd5