diff --git a/channeld/channeld.c b/channeld/channeld.c index a38cb50ddbf0..e9f9ead02521 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1883,12 +1883,30 @@ static bool have_i_signed_inflight(const struct peer *peer, return has_sig; } +/* psbt_finalize moves both signatures on the shared input into its witness */ +static bool shared_input_finalized(const struct peer *peer, + const struct inflight *inflight) +{ + u32 index; + + if (!inflight || !inflight->psbt) + return false; + + index = find_channel_funding_input(inflight->psbt, + &peer->channel->funding); + + return inflight->psbt->inputs[index].final_witness != NULL; +} + static bool have_they_signed_inflight(const struct peer *peer, const struct inflight *inflight) { bool has_sig; u32 index; + if (!inflight || !inflight->psbt) + return false; + index = find_channel_funding_input(inflight->psbt, &peer->channel->funding); @@ -1978,12 +1996,18 @@ static void check_tx_abort(struct peer *peer, const u8 *msg, struct bitcoin_txid * A sending node: * - MUST NOT have already transmitted `tx_signatures` */ - /* As initiator we sign the splice we are negotiating before - * sending anything, so for it i_sent_sigs decides. Any other - * inflight, including this one after a reconnect, may predate - * that field, so our signature on it counts as sent. */ + /* As initiator we sign the shared input when the inflight is + * created, before sending anything, so our signature alone + * does not mean we sent it. An accepter only signs as it + * sends tx_signatures. Once they have signed, the tx may be + * complete elsewhere (older builds could clear i_sent_sigs, + * and a multi-channel splice carries it to other peers), so + * that also counts as sent. */ if (itr->i_sent_sigs - || (!(peer->splicing && itr == last_inflight(peer)) + || itr->remote_tx_sigs + || have_they_signed_inflight(peer, itr) + || shared_input_finalized(peer, itr) + || (!itr->i_am_initiator && have_i_signed_inflight(peer, itr))) { peer_failed_err(peer->pps, &peer->channel_id, "tx_abort" " is not allowed after I have sent my" @@ -4167,8 +4191,10 @@ static void resume_splice_negotiation(struct peer *peer, inflight->i_sent_sigs); wire_sync_write(MASTER_FD, take(msg)); - msg = towire_channeld_splice_sending_sigs(tmpctx, &final_txid); - wire_sync_write(MASTER_FD, take(msg)); + /* Only send once lightningd has stored i_sent_sigs */ + msg = towire_channeld_splice_sending_sigs(NULL, &final_txid); + master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_SPLICE_SENDING_SIGS_REPLY); peer_write(peer->pps, sigmsg); } @@ -4363,8 +4389,10 @@ static void resume_splice_negotiation(struct peer *peer, if (!do_i_send_tx_sigs_first(peer, current_psbt, our_role, inflight->force_sign_first) && send_signature) { - msg = towire_channeld_splice_sending_sigs(tmpctx, &final_txid); - wire_sync_write(MASTER_FD, take(msg)); + /* Only send once lightningd has stored i_sent_sigs */ + msg = towire_channeld_splice_sending_sigs(NULL, &final_txid); + master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_SPLICE_SENDING_SIGS_REPLY); peer_write(peer->pps, sigmsg); status_debug("Splice: we signed second"); @@ -4678,6 +4706,7 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg) new_inflight->force_sign_first = peer->splicing->force_sign_first; new_inflight->locked_scid = NULL; new_inflight->i_sent_sigs = false; + new_inflight->remote_tx_sigs = false; current_push_val = relative_splice_balance_fundee(peer, our_role,ictx->current_psbt, outpoint.n, splice_funding_index); @@ -4981,6 +5010,7 @@ static void splice_initiator_user_finalized(struct peer *peer) new_inflight->force_sign_first = peer->splicing->force_sign_first; new_inflight->locked_scid = NULL; new_inflight->i_sent_sigs = false; + new_inflight->remote_tx_sigs = false; audit_psbt(ictx->current_psbt, ictx->current_psbt); @@ -7129,6 +7159,7 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_ADD_INFLIGHT: case WIRE_CHANNELD_UPDATE_INFLIGHT: case WIRE_CHANNELD_GOT_INFLIGHT: + case WIRE_CHANNELD_SPLICE_SENDING_SIGS_REPLY: case WIRE_CHANNELD_SPLICE_STATE_ERROR: case WIRE_CHANNELD_LOCAL_ANCHOR_INFO: case WIRE_CHANNELD_REESTABLISHED: diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 96e3f95d0559..339cb55fdeaf 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -258,6 +258,9 @@ msgdata,channeld_splice_confirmed_signed,output_index,u32, msgtype,channeld_splice_sending_sigs,7214 msgdata,channeld_splice_sending_sigs,tx,bitcoin_txid, +# master->channeld: Inflight saved, signatures can be sent +msgtype,channeld_splice_sending_sigs_reply,7314 + # channeld->master: A feerate error has occured msgtype,channeld_splice_feerate_error,7215 msgdata,channeld_splice_feerate_error,fee,amount_msat, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 09890a363f71..e948f212fafd 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -350,16 +350,24 @@ static void handle_splice_abort(struct lightningd *ld, inflight = list_tail(&channel->inflights, struct channel_inflight, list); - - if (!bitcoin_outpoint_eq(outpoint, - &inflight->funding->outpoint)) + if (!inflight + || !bitcoin_outpoint_eq(outpoint, + &inflight->funding->outpoint)) { + cc = splice_command_for_chan(ld, channel); + if (cc) + was_pending(command_fail(cc->cmd, SPLICE_ABORT, + "%s", reason)); channel_internal_error(channel, "abort outpoint %s does not" " match ours %s", fmt_bitcoin_outpoint(tmpctx, outpoint), - fmt_bitcoin_outpoint(tmpctx, - &inflight->funding->outpoint)); + inflight + ? fmt_bitcoin_outpoint(tmpctx, + &inflight->funding->outpoint) + : "(none)"); + return; + } wallet_inflight_del(ld->wallet, channel, inflight); tal_free(inflight); @@ -818,6 +826,16 @@ static void handle_splice_sending_sigs(struct lightningd *ld, "Splice signatures sent"); watch_splice_inflight(ld, inflight); + + if (ld->dev_splice_exit_before_sending_sigs) { + log_unusual(channel->log, "dev-splice-exit-before-sending-sigs:" + " exiting before replying to channeld"); + exit(1); + } + + /* subd only writes this after the transaction commits */ + subd_send_msg(channel->owner, + take(towire_channeld_splice_sending_sigs_reply(NULL))); } static void scid_updated(struct channel *channel) @@ -1737,6 +1755,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_DEV_MEMLEAK: case WIRE_CHANNELD_DEV_QUIESCE: case WIRE_CHANNELD_GOT_INFLIGHT: + case WIRE_CHANNELD_SPLICE_SENDING_SIGS_REPLY: case WIRE_CHANNELD_DEV_PEER_SHACHAIN: /* Replies go to requests. */ case WIRE_CHANNELD_OFFER_HTLC_REPLY: diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index b6c054e0bfe8..a0b0cc14a41e 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -150,6 +150,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_splice_abort_at_tx_complete = false; ld->dev_splice_corrupt_shared_sig = false; ld->dev_splice_corrupt_input_witness = false; + ld->dev_splice_exit_before_sending_sigs = false; ld->dev_splice_sign_second = false; ld->dev_allow_shutdown_destination_change = false; ld->dev_hsmd_no_preapprove_check = false; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 3427cc138f20..dbbf247415bd 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -383,6 +383,10 @@ struct lightningd { * check can be tested. */ bool dev_splice_corrupt_input_witness; + /* Exit while handling splice_sending_sigs, after i_sent_sigs is + * stored but before replying to channeld. */ + bool dev_splice_exit_before_sending_sigs; + /* Never send splice tx_signatures first, so the checks we make * before signing can be tested. */ bool dev_splice_sign_second; diff --git a/lightningd/options.c b/lightningd/options.c index 643d40f98a5c..83345ffe2729 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -925,6 +925,10 @@ static void dev_register_opts(struct lightningd *ld) opt_set_bool, &ld->dev_splice_corrupt_input_witness, "Send splice tx_signatures first, with corrupted witnesses for our inputs"); + clnopt_noarg("--dev-splice-exit-before-sending-sigs", OPT_DEV, + opt_set_bool, + &ld->dev_splice_exit_before_sending_sigs, + "Exit after storing i_sent_sigs, before replying to channeld"); clnopt_noarg("--dev-splice-sign-second", OPT_DEV, opt_set_bool, &ld->dev_splice_sign_second, diff --git a/tests/plugins/channeld_fakenet.c b/tests/plugins/channeld_fakenet.c index 23a51a9c4fa6..ef65cc802960 100644 --- a/tests/plugins/channeld_fakenet.c +++ b/tests/plugins/channeld_fakenet.c @@ -1239,6 +1239,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CHANNELD_SPLICE_CONFIRMED_INIT: case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED: case WIRE_CHANNELD_SPLICE_SENDING_SIGS: + case WIRE_CHANNELD_SPLICE_SENDING_SIGS_REPLY: case WIRE_CHANNELD_SPLICE_CONFIRMED_UPDATE: case WIRE_CHANNELD_SPLICE_LOOKUP_TX: case WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT: diff --git a/tests/test_splicing.py b/tests/test_splicing.py index 1983c7fa38a4..461265fc04df 100644 --- a/tests/test_splicing.py +++ b/tests/test_splicing.py @@ -96,7 +96,7 @@ def test_splice_stored_feerate_repaired_on_upgrade(node_factory, bitcoind, # They are plain idempotent UPDATEs, so re-running them is safe. The # count has to reach back past both of them, so it grows whenever a # migration is appended after them. - l1.db_manip("UPDATE version SET version = version - 3") + l1.db_manip("UPDATE version SET version = version - 4") l1.daemon.opts['database-upgrade'] = 'true' l1.start() @@ -1311,6 +1311,214 @@ def test_splice_peer_aborts_after_tx_complete(node_factory, bitcoind): l1.rpc.xpay(l2.rpc.invoice(10000, 'after-abort', 'after-abort')['bolt11']) +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +def test_splice_peer_aborts_unsigned_after_reconnect(node_factory, bitcoind, executor): + l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, + opts={'may_reconnect': True, + 'allow_warning': True}) + chan_id = l1.get_channel_id(l2) + + # l2 never gets l1's last tx_complete, so only l1 stores the inflight, + # with its signature on the shared input but no tx_signatures sent. + l1.stop() + l1.disconnect = ['$WIRE_TX_COMPLETE'] + l1.daemon.opts['dev-disconnect'] = os.path.join(l1.daemon.lightning_dir, + TEST_NETWORK, 'dev-disconnect') + l1.start() + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['peer_connected']) + l1.daemon.wait_for_log(r'billboard: Channel ready for use.') + + funds_result = l1.rpc.addpsbtoutput(100000) + result = l1.rpc.splice_init(chan_id, -105801, funds_result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + assert result['commitments_secured'] is False + executor.submit(l1.rpc.splice_update, chan_id, result['psbt']) + l1.daemon.wait_for_log(r'dev_disconnect: \$WIRE_TX_COMPLETE') + wait_for(lambda: l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 1) + + assert l1.db_query("SELECT i_sent_sigs FROM channel_funding_inflights;")[0]['i_sent_sigs'] == 0 + assert l2.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 0 + + # On reconnect l2 does not know the splice and answers with tx_abort. + l1.rpc.disconnect(l2.info['id'], force=True) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.daemon.wait_for_log(r'peer_in WIRE_TX_ABORT') + l1.daemon.wait_for_log(r'Restarting channeld after tx_abort on CHANNELD_NORMAL channel' + r'|Peer permanent failure') + assert not l1.daemon.is_in_log('Peer permanent failure') + assert not l2.daemon.is_in_log('Peer permanent failure') + + wait_for(lambda: l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 0) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['peer_connected']) + assert only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL' + l1.rpc.xpay(l2.rpc.invoice(10000, 'after-abort', 'after-abort')['bolt11']) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "modifies database, which is assumed sqlite3") +def test_splice_tx_abort_refused_with_their_sigs(node_factory, bitcoind): + l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, + opts={'may_reconnect': True, + 'allow_warning': True}) + chan_id = l1.get_channel_id(l2) + + funds_result = l1.rpc.fundpsbt("109000sat", 0, 0, excess_as_change=True) + result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + assert result['commitments_secured'] is True + result = l1.rpc.signpsbt(result['psbt']) + splice_txid = l1.rpc.splice_signed(chan_id, result['signed_psbt'])['txid'] + l1.daemon.wait_for_log(' to CHANNELD_AWAITING_SPLICE') + l2.daemon.wait_for_log(' to CHANNELD_AWAITING_SPLICE') + wait_for(lambda: splice_txid in bitcoind.rpc.getrawmempool()) + + # l1 holds both signatures, but its inflight says it neither sent nor + # received tx_signatures, as an older build could leave it. l2 has + # forgotten the splice, so it answers l1's next_funding with tx_abort. + l1.stop() + l2.stop() + l1.db_manip("UPDATE channel_funding_inflights SET i_sent_sigs=0," + " funding_tx_remote_sigs_received=0") + l2.db_manip("DELETE FROM htlc_sigs WHERE inflight_tx_id IS NOT NULL") + l2.db_manip("DELETE FROM channel_funding_inflights") + l2.db_manip("UPDATE channels SET state=3 WHERE state=13") + l2.start() + l1.start() + + l1.daemon.wait_for_log(r'peer_in WIRE_TX_ABORT') + l1.daemon.wait_for_log(r'tx_abort is not allowed after I have sent my signature' + r'|Restarting channeld after tx_abort') + assert l1.daemon.is_in_log(r'tx_abort is not allowed after I have sent my signature') + assert only_one(l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;"))['c'] == 1 + + # l2 has no record of the splice left to follow it onchain. + l2.stop() + + # l1 keeps watching the splice, so its close of the splice output + # resolves as its own. + wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 2) + assert splice_txid in bitcoind.rpc.getrawmempool() + bitcoind.generate_block(1) + l1.daemon.wait_for_log(r'Splice candidate {}:[0-9]+ spent before splice_locked; adopting it'.format(splice_txid)) + l1.daemon.wait_for_log(r'Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by OUR_UNILATERAL') + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "modifies database, which is assumed sqlite3") +def test_splice_sent_sigs_marked_on_upgrade(node_factory, bitcoind, executor): + l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, + opts={'may_reconnect': True, + 'allow_warning': True, + 'dev-no-reconnect': None}) + chan_id = l1.get_channel_id(l2) + + # l2 withholds its tx_signatures, so l1 holds no signature of l2's. + l2.stop() + l2.disconnect = ['-WIRE_TX_SIGNATURES'] + l2.daemon.opts['dev-disconnect'] = os.path.join(l2.daemon.lightning_dir, + TEST_NETWORK, 'dev-disconnect') + l2.start() + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['peer_connected']) + + funds_result = l1.rpc.addpsbtoutput(100000) + result = l1.rpc.splice_init(chan_id, -105801, funds_result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + executor.submit(l1.rpc.splice_update, chan_id, result['psbt']) + l2.daemon.wait_for_log(r'dev_disconnect: -WIRE_TX_SIGNATURES') + wait_for(lambda: l1.db_query("SELECT count(*) as c FROM channel_funding_inflights" + " WHERE last_tx IS NOT NULL;")[0]['c'] == 1) + l1.stop() + l2.stop() + + # An older release that had sent our tx_signatures here stored the same + # row, with i_sent_sigs still 0. Rewind so the upgrade marks it. + row = only_one(l1.db_query("SELECT i_am_initiator, i_sent_sigs" + " FROM channel_funding_inflights;")) + assert row['i_am_initiator'] == 1 and row['i_sent_sigs'] == 0 + l1.db_manip("UPDATE version SET version = version - 1") + l1.daemon.opts['database-upgrade'] = 'true' + + # l2 forgets the splice, so it answers l1's next_funding with tx_abort. + l2.db_manip("DELETE FROM htlc_sigs WHERE inflight_tx_id IS NOT NULL") + l2.db_manip("DELETE FROM channel_funding_inflights") + l2.db_manip("UPDATE channels SET state=3 WHERE state=13") + del l2.daemon.opts['dev-disconnect'] + l2.start() + l1.start() + assert l1.daemon.is_in_log(r'Updating database from version') + assert only_one(l1.db_query("SELECT i_sent_sigs" + " FROM channel_funding_inflights;"))['i_sent_sigs'] == 1 + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + l1.daemon.wait_for_log(r'peer_in WIRE_TX_ABORT') + l1.daemon.wait_for_log(r'tx_abort is not allowed after I have sent my signature' + r'|Restarting channeld after tx_abort') + assert l1.daemon.is_in_log(r'tx_abort is not allowed after I have sent my signature') + assert only_one(l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;"))['c'] == 1 + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.parametrize("l1_initiates", [True, False]) +def test_splice_sigs_sent_after_commit(node_factory, bitcoind, executor, l1_initiates): + l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=True, + opts=[{'may_reconnect': True, + 'may_fail': True, + 'dev-splice-exit-before-sending-sigs': None}, + {'may_reconnect': True}]) + chan_id = l1.get_channel_id(l2) + + # The initiator adds the shared input, so the accepter sends + # tx_signatures first: l1 sends second when it initiates, first when not. + if l1_initiates: + l2_log_start = len(l2.daemon.logs) + funds_result = l1.rpc.fundpsbt("109000sat", 0, 0, excess_as_change=True) + result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + assert result['commitments_secured'] is True + result = l1.rpc.signpsbt(result['psbt']) + executor.submit(l1.rpc.splice_signed, chan_id, result['signed_psbt']) + else: + l1.rpc.xpay(l2.rpc.invoice(300000000, 'push', 'push')['bolt11']) + l2_log_start = len(l2.daemon.logs) + funds_result = l2.rpc.addpsbtoutput(100000) + result = l2.rpc.splice_init(chan_id, -105801, funds_result['psbt']) + result = l2.rpc.splice_update(chan_id, result['psbt']) + executor.submit(l2.rpc.splice_update, chan_id, result['psbt']) + + # lightningd exits before replying, so channeld must not send. + l1.daemon.wait_for_log(r'dev-splice-exit-before-sending-sigs') + wait_for(lambda: l1.daemon.proc.poll() is not None) + time.sleep(1) + assert not l2.daemon.is_in_log(r'channeld-chan#[0-9]*: peer_in WIRE_TX_SIGNATURES', + start=l2_log_start) + + del l1.daemon.opts['dev-splice-exit-before-sending-sigs'] + l1.start() + l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH') + + bitcoind.generate_block(6, wait_for_mempool=1) + l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + + assert not l1.daemon.is_in_log('Peer permanent failure') + assert not l2.daemon.is_in_log('Peer permanent failure') + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['peer_connected']) + assert only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CHANNELD_NORMAL' + l1.rpc.xpay(l2.rpc.invoice(10000, 'after-restart', 'after-restart')['bolt11']) + + @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') diff --git a/wallet/migrations.c b/wallet/migrations.c index 99d7027ab8f7..2e2798f516a6 100644 --- a/wallet/migrations.c +++ b/wallet/migrations.c @@ -1120,6 +1120,25 @@ static const struct db_migration dbmigrations[] = { /* Clamping is idempotent, so no revert needed */ NULL, NULL}, {NULL, migrate_channel_type_unified_sigs_bit}, + /* Until wallet_inflight_save wrote i_sent_sigs, it stayed 0 even once + * we had sent tx_signatures, so for a splice we initiated it cannot + * tell us whether the peer can complete the tx. + * Mark every such inflight as sent, on channels still in + * CHANNELD_NORMAL (3) or CHANNELD_AWAITING_SPLICE (13): numbers spelled + * out so the migration keeps doing what it did when it shipped. + * + * These releases already refused a peer's tx_abort for such an + * inflight, since we sign the shared input as initiator when the + * inflight is created, so this keeps that. Accepter inflights are left + * alone, as an accepter's own signature on the shared input still + * counts as sent. */ + {SQL("UPDATE channel_funding_inflights" + " SET i_sent_sigs = 1" + " WHERE i_am_initiator = 1" + " AND channel_id IN (SELECT id FROM channels" + " WHERE state = 3 OR state = 13);"), NULL, + /* Only sets a flag older releases also read, so no revert needed */ + NULL, NULL}, }; const struct db_migration *get_db_migrations(size_t *num)