diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index d89138d32..0d8b464aa 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -543,6 +543,13 @@ static void rbf_channel_hook_cb(struct rbf_channel_payload *payload STEALS) return subd_send_msg(dualopend, take(msg)); } + /* Update the remote's require confirmed preferences */ + if (payload->req_confirmed_ins_remote != channel->req_confirmed_ins[REMOTE]) { + channel->req_confirmed_ins[REMOTE] = + payload->req_confirmed_ins_remote; + wallet_channel_save(dualopend->ld->wallet, channel); + } + /* Update channel with new open attempt. */ channel->open_attempt = new_channel_open_attempt(channel); msg = towire_dualopend_got_rbf_offer_reply(NULL, @@ -2002,7 +2009,8 @@ static void rbf_got_offer(struct subd *dualopend, const u8 *msg) &payload->our_last_funding, &payload->funding_feerate_per_kw, &payload->locktime, - &payload->requested_lease_amt)) { + &payload->requested_lease_amt, + &payload->req_confirmed_ins_remote)) { channel_internal_error(channel, "Bad WIRE_DUALOPEND_GOT_RBF_OFFER: %s", tal_hex(msg, msg)); @@ -2027,8 +2035,6 @@ static void rbf_got_offer(struct subd *dualopend, const u8 *msg) payload->peer_id = channel->peer->id; payload->feerate_our_max = feerate_max(dualopend->ld, NULL); payload->feerate_our_min = feerate_min(dualopend->ld, NULL); - payload->req_confirmed_ins_remote = - channel->req_confirmed_ins[REMOTE]; payload->psbt = NULL; @@ -3243,6 +3249,25 @@ done: tal_free(pv); } +static void handle_update_require_confirmed(struct subd *dualopend, + const u8 *msg) +{ + bool require_confirmed; + struct channel *channel = dualopend->channel; + + if (!fromwire_dualopend_update_require_confirmed(msg, &require_confirmed)) { + channel_internal_error(dualopend->channel, + "Bad DUALOPEND_UPDATE_REQUIRE_CONFIRMED: %s", + tal_hex(msg, msg)); + return; + } + + if (channel->req_confirmed_ins[REMOTE] != require_confirmed) { + channel->req_confirmed_ins[REMOTE] = require_confirmed; + wallet_channel_save(dualopend->ld->wallet, channel); + } +} + static void handle_validate_inputs(struct subd *dualopend, const u8 *msg) { @@ -3632,6 +3657,9 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_VALIDATE_INPUTS: handle_validate_inputs(dualopend, msg); return 0; + case WIRE_DUALOPEND_UPDATE_REQUIRE_CONFIRMED: + handle_update_require_confirmed(dualopend, msg); + return 0; /* Messages we send */ case WIRE_DUALOPEND_INIT: case WIRE_DUALOPEND_REINIT: diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 8d1cf58d2..cc5304588 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3564,6 +3564,14 @@ static void rbf_local_start(struct state *state, u8 *msg) *init_rbf_tlvs->funding_output_contribution = (s64)tx_state->opener_funding.satoshis; /* Raw: wire conversion */ + + /* We repeat whatever we used on initial open for requiring confirmed + * inputs */ + if (state->require_confirmed_inputs[LOCAL]) + init_rbf_tlvs->require_confirmed_inputs = + tal(init_rbf_tlvs, struct tlv_tx_init_rbf_tlvs_require_confirmed_inputs); + + msg = towire_tx_init_rbf(tmpctx, &state->channel_id, tx_state->tx_locktime, tx_state->feerate_per_kw_funding, @@ -3601,6 +3609,14 @@ static void rbf_local_start(struct state *state, u8 *msg) } else tx_state->accepter_funding = state->tx_state->accepter_funding; + /* Set the require_confirmed_inputs to whatever they set here */ + if (ack_rbf_tlvs) + state->require_confirmed_inputs[REMOTE] = + ack_rbf_tlvs->require_confirmed_inputs != NULL; + else + /* They have to re-affirm to keep it! */ + state->require_confirmed_inputs[REMOTE] = false; + /* Check that total funding doesn't overflow */ if (!amount_sat_add(&total, tx_state->opener_funding, tx_state->accepter_funding)) { @@ -3672,6 +3688,11 @@ static void rbf_local_start(struct state *state, u8 *msg) tal_free(state->tx_state); state->tx_state = tal_steal(state, tx_state); + /* Notify lightningd about require_confirmed state */ + msg = towire_dualopend_update_require_confirmed(NULL, + state->require_confirmed_inputs[REMOTE]); + wire_sync_write(REQ_FD, take(msg)); + /* We merge with RBF's we've initiated now */ rbf_wrap_up(state, tx_state, total); tal_free(rbf_ctx); @@ -3735,6 +3756,14 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) /* Otherwise we use the last known funding amount */ tx_state->opener_funding = state->tx_state->opener_funding; + /* Set the require_confirmed_inputs to whatever they set here */ + if (init_rbf_tlvs) + state->require_confirmed_inputs[REMOTE] = + init_rbf_tlvs->require_confirmed_inputs != NULL; + else + /* They have to re-affirm to keep it! */ + state->require_confirmed_inputs[REMOTE] = false; + /* Copy over the channel config info -- everything except * the reserve will be the same */ tx_state->localconf = state->tx_state->localconf; @@ -3757,7 +3786,8 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) state->tx_state->accepter_funding, tx_state->feerate_per_kw_funding, tx_state->tx_locktime, - state->requested_lease); + state->requested_lease, + state->require_confirmed_inputs[REMOTE]); wire_sync_write(REQ_FD, take(msg)); msg = wire_sync_read(tmpctx, REQ_FD); @@ -3833,6 +3863,11 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) *ack_rbf_tlvs->funding_output_contribution = (s64)tx_state->accepter_funding.satoshis; /* Raw: wire conversion */ + /* We keep whatever we initially set at open for RBFs */ + if (state->require_confirmed_inputs[LOCAL]) + ack_rbf_tlvs->require_confirmed_inputs = + tal(ack_rbf_tlvs, struct tlv_tx_ack_rbf_tlvs_require_confirmed_inputs); + msg = towire_tx_ack_rbf(tmpctx, &state->channel_id, ack_rbf_tlvs); peer_write(state->pps, msg); peer_billboard(false, "channel rbf: ack sent, waiting for reply"); @@ -4177,6 +4212,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_DUALOPEND_DRY_RUN: case WIRE_DUALOPEND_VALIDATE_LEASE: case WIRE_DUALOPEND_VALIDATE_INPUTS: + case WIRE_DUALOPEND_UPDATE_REQUIRE_CONFIRMED: break; } status_failed(STATUS_FAIL_MASTER_IO, diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 01c8b70c3..8d8915026 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -118,12 +118,17 @@ msgdata,dualopend_got_rbf_offer,our_last_funding,amount_sat, msgdata,dualopend_got_rbf_offer,funding_feerate_per_kw,u32, msgdata,dualopend_got_rbf_offer,locktime,u32, msgdata,dualopend_got_rbf_offer,requested_lease,?amount_sat, +msgdata,dualopend_got_rbf_offer,require_confirmed_inputs,bool, # master->dualopend: reply back with our funding info/contribs msgtype,dualopend_got_rbf_offer_reply,7505 msgdata,dualopend_got_rbf_offer_reply,our_funding,amount_sat, msgdata,dualopend_got_rbf_offer_reply,psbt,wally_psbt, +# dualopend->master: update to require_confirmed_inputs preference +msgtype,dualopend_update_require_confirmed,7511 +msgdata,dualopend_update_require_confirmed,require_confirmed_inputs,bool, + # dualopend->master: is this a valid RBF candidate transaction? msgtype,dualopend_rbf_validate,7506 msgdata,dualopend_rbf_validate,proposed_funding_psbt,wally_psbt, diff --git a/tests/fuzz/fuzz-wire-tx_ack_rbf.c b/tests/fuzz/fuzz-wire-tx_ack_rbf.c index e00053663..838f6ab9e 100644 --- a/tests/fuzz/fuzz-wire-tx_ack_rbf.c +++ b/tests/fuzz/fuzz-wire-tx_ack_rbf.c @@ -31,10 +31,14 @@ static bool equal(const struct tx_ack_rbf *x, const struct tx_ack_rbf *y) return false; assert(x->tlvs && y->tlvs); - return memeq(x->tlvs->funding_output_contribution, - tal_bytelen(x->tlvs->funding_output_contribution), - y->tlvs->funding_output_contribution, - tal_bytelen(y->tlvs->funding_output_contribution)); + if (!memeq(x->tlvs->funding_output_contribution, + tal_bytelen(x->tlvs->funding_output_contribution), + y->tlvs->funding_output_contribution, + tal_bytelen(y->tlvs->funding_output_contribution))) + return false; + + return !!x->tlvs->require_confirmed_inputs == + !!y->tlvs->require_confirmed_inputs; } void run(const u8 *data, size_t size) diff --git a/tests/fuzz/fuzz-wire-tx_init_rbf.c b/tests/fuzz/fuzz-wire-tx_init_rbf.c index 5e52368a9..c58107608 100644 --- a/tests/fuzz/fuzz-wire-tx_init_rbf.c +++ b/tests/fuzz/fuzz-wire-tx_init_rbf.c @@ -36,10 +36,14 @@ static bool equal(const struct tx_init_rbf *x, const struct tx_init_rbf *y) return false; assert(x->tlvs && y->tlvs); - return memeq(x->tlvs->funding_output_contribution, + if (!memeq(x->tlvs->funding_output_contribution, tal_bytelen(x->tlvs->funding_output_contribution), y->tlvs->funding_output_contribution, - tal_bytelen(y->tlvs->funding_output_contribution)); + tal_bytelen(y->tlvs->funding_output_contribution))) + return false; + + return !!x->tlvs->require_confirmed_inputs == + !!y->tlvs->require_confirmed_inputs; } void run(const u8 *data, size_t size) diff --git a/wallet/wallet.c b/wallet/wallet.c index 1303e9d9c..91ed55ca8 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2286,9 +2286,10 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " remote_feerate_ppm=?," // 48 " remote_cltv_expiry_delta=?," // 49 " remote_htlc_minimum_msat=?," // 50 - " remote_htlc_maximum_msat=?," - " last_stable_connection=?" - " WHERE id=?")); + " remote_htlc_maximum_msat=?," // 51 + " last_stable_connection=?" // 52 + " require_confirm_inputs_remote=?" // 53 + " WHERE id=?")); // 54 db_bind_u64(stmt, chan->their_shachain.id); if (chan->scid) db_bind_short_channel_id(stmt, chan->scid); @@ -2385,6 +2386,8 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt); } db_bind_u64(stmt, chan->last_stable_connection); + + db_bind_int(stmt, chan->req_confirmed_ins[REMOTE]); db_bind_u64(stmt, chan->dbid); db_exec_prepared_v2(take(stmt)); diff --git a/wire/extracted_peer_03_dual-funding.patch b/wire/extracted_peer_03_dual-funding.patch index 03fe08d99..d8713f4f6 100644 --- a/wire/extracted_peer_03_dual-funding.patch +++ b/wire/extracted_peer_03_dual-funding.patch @@ -1,6 +1,6 @@ --- wire/peer_wire.csv.raw 2023-09-11 13:32:30.677617251 +0930 +++ wire/peer_wire.csv.openchannel2 2023-09-11 13:31:47.906308397 +0930 -@@ -37,6 +37,51 @@ +@@ -37,6 +37,53 @@ tlvdata,n2,tlv1,amount_msat,tu64, tlvtype,n2,tlv2,11 tlvdata,n2,tlv2,cltv_expiry,tu32, @@ -40,11 +40,13 @@ +msgdata,tx_init_rbf,tlvs,tx_init_rbf_tlvs, +tlvtype,tx_init_rbf_tlvs,funding_output_contribution,0 +tlvdata,tx_init_rbf_tlvs,funding_output_contribution,satoshis,s64, ++tlvtype,tx_init_rbf_tlvs,require_confirmed_inputs,2 +msgtype,tx_ack_rbf,73 +msgdata,tx_ack_rbf,channel_id,channel_id, +msgdata,tx_ack_rbf,tlvs,tx_ack_rbf_tlvs, +tlvtype,tx_ack_rbf_tlvs,funding_output_contribution,0 +tlvdata,tx_ack_rbf_tlvs,funding_output_contribution,satoshis,s64, ++tlvtype,tx_ack_rbf_tlvs,require_confirmed_inputs,2 +msgtype,tx_abort,74 +msgdata,tx_abort,channel_id,channel_id, +msgdata,tx_abort,len,u16, diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 063d8cf07..b29a70153 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -76,11 +76,13 @@ msgdata,tx_init_rbf,feerate,u32, msgdata,tx_init_rbf,tlvs,tx_init_rbf_tlvs, tlvtype,tx_init_rbf_tlvs,funding_output_contribution,0 tlvdata,tx_init_rbf_tlvs,funding_output_contribution,satoshis,s64, +tlvtype,tx_init_rbf_tlvs,require_confirmed_inputs,2 msgtype,tx_ack_rbf,73 msgdata,tx_ack_rbf,channel_id,channel_id, msgdata,tx_ack_rbf,tlvs,tx_ack_rbf_tlvs, tlvtype,tx_ack_rbf_tlvs,funding_output_contribution,0 tlvdata,tx_ack_rbf_tlvs,funding_output_contribution,satoshis,s64, +tlvtype,tx_ack_rbf_tlvs,require_confirmed_inputs,2 msgtype,tx_abort,74 msgdata,tx_abort,channel_id,channel_id, msgdata,tx_abort,len,u16,