From b8a2c396c77b8904d42d90d5aaecdd34896eaf23 Mon Sep 17 00:00:00 2001 From: Dusty Daemon Date: Mon, 22 Jan 2024 17:43:05 -0500 Subject: [PATCH] splice: Add support for tx_abort to channeld Add checking for and sending tx_abort to channeld. When receiving it we first ACK it back, send a request to restart to lightningd, and then shutdown channeld. We also must update the splice tests that relied on reconnect checks for splice warnings (as some are now tx_aborts instead). --- channeld/channeld.c | 384 +++++++++++++++++++++--------- tests/test_splicing.py | 4 +- tests/test_splicing_disconnect.py | 11 +- 3 files changed, 272 insertions(+), 127 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 71457c3fe..95c3a35cc 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -273,6 +273,12 @@ static void maybe_send_stfu(struct peer *peer) } } +/* Durring reestablish, STFU mode is assumed if continuing a splice */ +static void assume_stfu_mode(struct peer *peer) +{ + peer->stfu_sent[LOCAL] = peer->stfu_sent[REMOTE] = true; +} + static void handle_stfu(struct peer *peer, const u8 *stfu) { struct channel_id channel_id; @@ -1625,6 +1631,197 @@ static void send_revocation(struct peer *peer, peer_write(peer->pps, take(msg)); } +static struct inflight *last_inflight(struct peer *peer) +{ + size_t count = tal_count(peer->splice_state->inflights); + + if (count) + return peer->splice_state->inflights[count - 1]; + + return NULL; +} + +static size_t last_inflight_index(struct peer *peer) +{ + assert(tal_count(peer->splice_state->inflights) > 0); + + return tal_count(peer->splice_state->inflights) - 1; +} + +static u32 find_channel_funding_input(struct wally_psbt *psbt, + struct bitcoin_outpoint *funding) +{ + for (size_t i = 0; i < psbt->num_inputs; i++) { + struct bitcoin_outpoint psbt_outpoint; + wally_psbt_input_get_outpoint(&psbt->inputs[i], &psbt_outpoint); + + if (!bitcoin_outpoint_eq(&psbt_outpoint, funding)) + continue; + + if (funding->n == psbt->inputs[i].index) + return i; + } + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to find splice funding tx"); + + return UINT_MAX; +} + +/* This checks if local has signed the funding input only */ +static bool have_i_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); + + if (!psbt_input_have_signature(inflight->psbt, index, + &peer->channel->funding_pubkey[LOCAL], + &has_sig)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable parse inflight psbt"); + + return has_sig; +} + +/* this checks if local has signed everything buy the funding input */ +static bool missing_user_signatures(const struct peer *peer, + const struct inflight *inflight) +{ + int sigs_needed; + u32 i, splice_funding_index; + const struct witness **outws; + enum tx_role our_role = inflight->i_am_initiator + ? TX_INITIATOR : TX_ACCEPTER; + + if (!inflight || !inflight->psbt) + return false; + + splice_funding_index = find_channel_funding_input(inflight->psbt, + &peer->channel->funding); + sigs_needed = 0; + for (i = 0; i < inflight->psbt->num_inputs; i++) { + struct wally_psbt_input *in = &inflight->psbt->inputs[i]; + u64 in_serial; + + if (!psbt_get_serial_id(&in->unknowns, &in_serial)) { + status_broken("PSBT input %"PRIu32" missing serial_id" + " %s", i, + type_to_string(tmpctx, + struct wally_psbt, + inflight->psbt)); + return true; + } + if (in_serial % 2 == our_role && i != splice_funding_index) + sigs_needed++; + } + + outws = psbt_to_witnesses(tmpctx, inflight->psbt, + our_role, splice_funding_index); + return tal_count(outws) != sigs_needed; +} + +static void check_tx_abort(struct peer *peer, const u8 *msg) +{ + struct inflight *inflight = last_inflight(peer); + struct bitcoin_outpoint *outpoint; + struct channel_id channel_id; + u8 *reason; + + if (!msg || fromwire_peektype(msg) != WIRE_TX_ABORT) + return; + + if (have_i_signed_inflight(peer, inflight)) { + peer_failed_err(peer->pps, &peer->channel_id, "tx_abort" + " is not allowed after I have sent my" + " signature. msg: %s", + tal_hex(tmpctx, msg)); + } + + if (!fromwire_tx_abort(tmpctx, msg, &channel_id, &reason)) + peer_failed_warn(peer->pps, &peer->channel_id, + "bad tx_abort %s", tal_hex(msg, msg)); + + status_info("Send ack of tx_abort"); + + peer_write(peer->pps, + take(towire_tx_abort(NULL, &peer->channel_id, NULL))); + + outpoint = NULL; + if (inflight) + outpoint = &inflight->outpoint; + + status_info("Send tx_abort to master"); + + wire_sync_write(MASTER_FD, + take(towire_channeld_splice_abort(NULL, false, + outpoint, + (char*)reason))); + + /* Give master a chance to pass the fd along */ + status_info("Delaying closing of master fd by 1 second"); + sleep(1); + + close(MASTER_FD); + exit(0); +} + +static void splice_abort(struct peer *peer, const char *fmt, ...) +{ + struct inflight *inflight = last_inflight(peer); + struct bitcoin_outpoint *outpoint; + u8 *msg; + char *reason; + va_list ap; + + va_start(ap, fmt); + reason = tal_vfmt(NULL, fmt, ap); + va_end(ap); + + if (have_i_signed_inflight(peer, inflight)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Tried to abort a splice where I have already" + " sent my signatures"); + + status_info("We are initiating tx_abort for reason: %s", reason); + + peer_write(peer->pps, + take(towire_tx_abort(NULL, &peer->channel_id, (u8*)reason))); + + do { + msg = peer_read(tmpctx, peer->pps); + if (handle_peer_error_or_warning(peer->pps, msg)) { + status_info("Received warning/error while expecting " + "tx_abort, msg: %s", tal_hex(tmpctx, msg)); + exit(0); + } + } while (fromwire_peektype(msg) != WIRE_TX_ABORT); + + status_info("We got TX_ABORT ack, now telling master about it"); + + outpoint = NULL; + if (inflight) + outpoint = &inflight->outpoint; + + msg = towire_channeld_splice_abort(tmpctx, true, outpoint, reason); + + wire_sync_write(MASTER_FD, msg); + + /* Give master a chance to pass the fd along */ + status_info("Delaying closing of master fd by 1 second"); + sleep(1); + + + close(MASTER_FD); + exit(0); +} + struct commitsig_info { struct commitsig *commitsig; struct secret *old_secret; @@ -1906,6 +2103,7 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer, s64 sub_splice_amnt = peer->splice_state->inflights[i]->splice_amnt; splice_msg = peer_read(tmpctx, peer->pps); + check_tx_abort(peer, splice_msg); /* Check type for cleaner failure message */ type = fromwire_peektype(msg); if (type != WIRE_COMMITMENT_SIGNED) @@ -2572,6 +2770,8 @@ static struct commitsig *interactive_send_commitments(struct peer *peer, WIRE_TX_SIGNATURES, WIRE_TX_ABORT); + check_tx_abort(peer, msg); + if (msg_received) *msg_received = msg; @@ -2831,7 +3031,7 @@ static struct amount_sat check_balances(struct peer *peer, out[TX_INITIATOR], true); wire_sync_write(MASTER_FD, take(msg)); - peer_failed_warn(peer->pps, &peer->channel_id, + splice_abort(peer, "Initiator funding is less than commited" " amount. Initiator contributing %s but they" " committed to %s. Pending offered HTLC" @@ -2853,7 +3053,7 @@ static struct amount_sat check_balances(struct peer *peer, out[TX_INITIATOR], true); wire_sync_write(MASTER_FD, take(msg)); - peer_failed_warn(peer->pps, &peer->channel_id, + splice_abort(peer, "Accepter funding is less than commited" " amount. Accepter contributing %s but they" " committed to %s. Pending offered HTLC" @@ -2895,8 +3095,7 @@ static struct amount_sat check_balances(struct peer *peer, msg = towire_channeld_splice_feerate_error(NULL, initiator_fee, false); wire_sync_write(MASTER_FD, take(msg)); - /* DTODO: Swap `peer_failed_warn` out for `tx_abort`? */ - peer_failed_warn(peer->pps, &peer->channel_id, + splice_abort(peer, "%s fee (%s) was too low, must be at least %s", opener ? "Our" : "Your", type_to_string(tmpctx, struct amount_msat, @@ -2909,8 +3108,7 @@ static struct amount_sat check_balances(struct peer *peer, msg = towire_channeld_splice_feerate_error(NULL, initiator_fee, true); wire_sync_write(MASTER_FD, take(msg)); - /* DTODO: Swap `peer_failed_warn` out for `tx_abort` */ - peer_failed_warn(peer->pps, &peer->channel_id, + splice_abort(peer, "Our own fee (%s) was too high, max without" " forcing is %s.", type_to_string(tmpctx, struct amount_msat, @@ -2923,8 +3121,7 @@ static struct amount_sat check_balances(struct peer *peer, msg = towire_channeld_splice_feerate_error(NULL, accepter_fee, false); wire_sync_write(MASTER_FD, take(msg)); - /* DTODO: Swap `peer_failed_warn` out for `tx_abort`? */ - peer_failed_warn(peer->pps, &peer->channel_id, + splice_abort(peer, "%s fee (%s) was too low, must be at least %s", opener ? "Your" : "Our", type_to_string(tmpctx, struct amount_msat, @@ -2937,8 +3134,7 @@ static struct amount_sat check_balances(struct peer *peer, msg = towire_channeld_splice_feerate_error(NULL, accepter_fee, true); wire_sync_write(MASTER_FD, take(msg)); - /* DTODO: Swap `peer_failed_warn` out for `tx_abort` */ - peer_failed_warn(peer->pps, &peer->channel_id, + splice_abort(peer, "Our own fee (%s) was too high, max without" " forcing is %s.", type_to_string(tmpctx, struct amount_msat, @@ -2991,26 +3187,6 @@ static struct amount_sat check_balances(struct peer *peer, return funding_amount_res; } -static u32 find_channel_funding_input(struct wally_psbt *psbt, - struct bitcoin_outpoint *funding) -{ - for (size_t i = 0; i < psbt->num_inputs; i++) { - struct bitcoin_outpoint psbt_outpoint; - wally_psbt_input_get_outpoint(&psbt->inputs[i], &psbt_outpoint); - - if (!bitcoin_outpoint_eq(&psbt_outpoint, funding)) - continue; - - if (funding->n == psbt->inputs[i].index) - return i; - } - - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Unable to find splice funding tx"); - - return UINT_MAX; -} - static void update_view_from_inflights(struct peer *peer) { struct inflight **inflights = peer->splice_state->inflights; @@ -3034,58 +3210,6 @@ static void update_view_from_inflights(struct peer *peer) } } -static struct inflight *last_inflight(struct peer *peer) -{ - size_t count = tal_count(peer->splice_state->inflights); - - if (count) - return peer->splice_state->inflights[count - 1]; - - return NULL; -} - -static size_t last_inflight_index(struct peer *peer) -{ - assert(tal_count(peer->splice_state->inflights) > 0); - - return tal_count(peer->splice_state->inflights) - 1; -} - -static bool have_i_signed_inflight(const struct peer *peer, - const struct inflight *inflight) -{ - bool has_sig; - u32 index; - - index = find_channel_funding_input(inflight->psbt, - &peer->channel->funding); - - if (!psbt_input_have_signature(inflight->psbt, index, - &peer->channel->funding_pubkey[LOCAL], - &has_sig)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Unable parse inflight psbt"); - - return has_sig; -} - -static bool check_tx_abort(struct peer *peer, const u8 *msg) -{ - if (!msg || fromwire_peektype(msg) != WIRE_TX_ABORT) - return false; - - if (have_i_signed_inflight(peer, last_inflight(peer))) { - peer_failed_err(peer->pps, &peer->channel_id, "tx_abort" - " is not allowed after I have sent my" - " signature. msg: %s", - tal_hex(tmpctx, msg)); - } - - /* DTODO: Remove last_inflight */ - - return true; -} - /* Called to finish an ongoing splice OR on restart from chanenl_reestablish. */ static void resume_splice_negotiation(struct peer *peer, bool send_commitments, @@ -3120,11 +3244,12 @@ static void resume_splice_negotiation(struct peer *peer, const u8 *msg_received; status_info("Splice negotation, will %ssend commit, %srecv commit," - " %ssend signature, %srecv signature", + " %ssend signature, %srecv signature as %s", send_commitments ? "" : "not ", recv_commitments ? "" : "not ", send_signature ? "" : "not ", - recv_signature ? "" : "not "); + recv_signature ? "" : "not ", + our_role == TX_INITIATOR ? "initiator" : "accepter"); wit_script = bitcoin_redeem_2of2(tmpctx, &peer->channel->funding_pubkey[LOCAL], @@ -3143,8 +3268,7 @@ static void resume_splice_negotiation(struct peer *peer, recv_commitments, &msg_received); - if (check_tx_abort(peer, msg_received)) - return; + check_tx_abort(peer, msg_received); if (their_commit) { if (inflight->last_tx != their_commit->tx) @@ -3246,8 +3370,7 @@ static void resume_splice_negotiation(struct peer *peer, type = fromwire_peektype(msg); - if (check_tx_abort(peer, msg)) - return; + check_tx_abort(peer, msg); if (handle_peer_error_or_warning(peer->pps, msg)) return; @@ -3457,6 +3580,7 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg) struct bitcoin_outpoint outpoint; struct amount_msat current_push_val; const enum tx_role our_role = TX_ACCEPTER; + u8 *abort_msg; /* Can't start a splice with another splice still active */ assert(!peer->splicing); @@ -3523,11 +3647,14 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg) ictx->pause_when_complete = false; error = process_interactivetx_updates(tmpctx, ictx, - &peer->splicing->received_tx_complete); + &peer->splicing->received_tx_complete, + &abort_msg); if (error) peer_failed_err(peer->pps, &peer->channel_id, "Interactive splicing error: %s", error); + check_tx_abort(peer, abort_msg); + assert(ictx->pause_when_complete == false); peer->splicing->sent_tx_complete = true; @@ -3621,6 +3748,7 @@ static void splice_initiator(struct peer *peer, const u8 *inmsg) u32 sequence = 0; u8 *scriptPubkey; char *error; + u8 *abort_msg; ictx = new_interactivetx_context(tmpctx, TX_INITIATOR, peer->pps, peer->channel_id); @@ -3711,12 +3839,15 @@ static void splice_initiator(struct peer *peer, const u8 *inmsg) error = process_interactivetx_updates(tmpctx, ictx, - &peer->splicing->received_tx_complete); + &peer->splicing->received_tx_complete, + &abort_msg); if (error) peer_failed_warn(peer->pps, &peer->channel_id, "Interactive splicing_ack error: %s", error); + check_tx_abort(peer, abort_msg); + peer->splicing->tx_add_input_count = ictx->tx_add_input_count; peer->splicing->tx_add_output_count = ictx->tx_add_output_count; @@ -3751,6 +3882,7 @@ static void splice_initiator_user_finalized(struct peer *peer) struct commitsig *their_commit; struct amount_msat current_push_val; const enum tx_role our_role = TX_INITIATOR; + u8 *abort_msg; ictx = new_interactivetx_context(tmpctx, our_role, peer->pps, peer->channel_id); @@ -3762,11 +3894,14 @@ static void splice_initiator_user_finalized(struct peer *peer) ictx->tx_add_output_count = peer->splicing->tx_add_output_count; error = process_interactivetx_updates(tmpctx, ictx, - &peer->splicing->received_tx_complete); + &peer->splicing->received_tx_complete, + &abort_msg); if (error) peer_failed_warn(peer->pps, &peer->channel_id, "Splice interactivetx error: %s", error); + check_tx_abort(peer, abort_msg); + /* With pause_when_complete fase, this assert should never fail */ assert(peer->splicing->received_tx_complete); peer->splicing->sent_tx_complete = true; @@ -3848,7 +3983,7 @@ static void splice_initiator_user_finalized(struct peer *peer) * new details to the active PSBT. Each user call enters here: */ static void splice_initiator_user_update(struct peer *peer, const u8 *inmsg) { - u8 *outmsg, *msg; + u8 *outmsg, *msg, *abort_msg; struct interactivetx_context *ictx; char *error; @@ -3897,11 +4032,14 @@ static void splice_initiator_user_update(struct peer *peer, const u8 *inmsg) } error = process_interactivetx_updates(tmpctx, ictx, - &peer->splicing->received_tx_complete); + &peer->splicing->received_tx_complete, + &abort_msg); if (error) peer_failed_warn(peer->pps, &peer->channel_id, "Splice update error: %s", error); + check_tx_abort(peer, abort_msg); + peer->splicing->tx_add_input_count = ictx->tx_add_input_count; peer->splicing->tx_add_output_count = ictx->tx_add_output_count; @@ -4087,6 +4225,15 @@ static void peer_in(struct peer *peer, const u8 *msg) if (handle_peer_error_or_warning(peer->pps, msg)) return; + check_tx_abort(peer, msg); + + /* If we're in STFU mode and aren't waiting for a STFU mode + * specific message, the only valid message was tx_abort */ + if (is_stfu_active(peer) && !peer->stfu_wait_single_msg) + peer_failed_warn(peer->pps, &peer->channel_id, + "Received message %s when only TX_ABORT was" + " valid", peer_wire_name(type)); + /* Must get channel_ready before almost anything. */ if (!peer->channel_ready[REMOTE]) { if (type != WIRE_CHANNEL_READY @@ -4691,17 +4838,23 @@ static void peer_reconnect(struct peer *peer, inflight = last_inflight(peer); if (inflight && (!inflight->last_tx || !inflight->remote_tx_sigs)) { - status_info("Reconnecting to peer with pending inflight commit:" - " %s, remote sigs: %s.", - inflight->last_tx ? "received" : "missing", - inflight->remote_tx_sigs ? "received" : "missing"); + if (missing_user_signatures(peer, inflight)) { + status_info("Unable to resume splice as user sigs are" + " missing."); + inflight = NULL; + } else { + status_info("Reconnecting to peer with pending inflight" + " commit: %s, remote sigs: %s.", + inflight->last_tx ? "received" : "missing", + inflight->remote_tx_sigs ? "received" : "missing"); - if (!send_tlvs) { - /* Subtle: we free tmpctx below as we loop, so tal off - * peer */ - send_tlvs = tlv_channel_reestablish_tlvs_new(peer); + if (!send_tlvs) { + /* Subtle: we free tmpctx below as we loop, so + * tal off peer */ + send_tlvs = tlv_channel_reestablish_tlvs_new(peer); + } + send_tlvs->next_funding = &inflight->outpoint.txid; } - send_tlvs->next_funding = &inflight->outpoint.txid; } /* BOLT #2: @@ -4806,10 +4959,6 @@ static void peer_reconnect(struct peer *peer, tal_count(peer->splice_state->inflights), peer->splice_state->count); - /* If we didn't send (i.e. don't support!) ignore theirs */ - if (!send_tlvs && !inflight) - recv_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); - local_next_funding = (send_tlvs ? send_tlvs->next_funding : NULL); remote_next_funding = (recv_tlvs ? recv_tlvs->next_funding : NULL); @@ -4823,6 +4972,7 @@ static void peer_reconnect(struct peer *peer, if (inflight && (remote_next_funding || local_next_funding)) { if (!remote_next_funding) { status_info("Resuming splice negotation."); + assume_stfu_mode(peer); resume_splice_negotiation(peer, false, true, @@ -4830,9 +4980,14 @@ static void peer_reconnect(struct peer *peer, true); } else if (bitcoin_txid_eq(remote_next_funding, &inflight->outpoint.txid)) { + /* Don't send sigs unless we have theirs */ + assert(local_next_funding || inflight->remote_tx_sigs); + status_info("Resuming splice negotation"); + if (local_next_funding) + assume_stfu_mode(peer); resume_splice_negotiation(peer, - !inflight->remote_tx_sigs, + true, local_next_funding, true, local_next_funding); @@ -4874,13 +5029,8 @@ static void peer_reconnect(struct peer *peer, take(towire_splice_locked(NULL, &peer->channel_id))); } else { - char *errmsg = tal_fmt(tmpctx, - "next_funding_txid not recognized." + splice_abort(peer, "next_funding_txid not recognized." " Sending tx_abort."); - peer_write(peer->pps, - take(towire_tx_abort(NULL, - &peer->channel_id, - (u8*)errmsg))); } } @@ -5656,6 +5806,8 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT: case WIRE_CHANNELD_SPLICE_FEERATE_ERROR: case WIRE_CHANNELD_SPLICE_FUNDING_ERROR: + case WIRE_CHANNELD_SPLICE_ABORT: + check_tx_abort(peer, msg); break; case WIRE_CHANNELD_DEV_REENABLE_COMMIT: if (peer->developer) { @@ -5967,11 +6119,6 @@ int main(int argc, char *argv[]) tptr = &timeout; } - /* If we're in STFU mode and aren't waiting for a STFU mode - * specific message, don't read from the peer. */ - if (is_stfu_active(peer) && !peer->stfu_wait_single_msg) - FD_CLR(peer->pps->peer_fd, &rfds); - if (select(nfds, &rfds, NULL, NULL, tptr) < 0) { /* Signals OK, eg. SIGUSR1 */ if (errno == EINTR) @@ -5999,5 +6146,6 @@ int main(int argc, char *argv[]) assert(shutdown_complete(peer)); send_shutdown_complete(peer); daemon_shutdown(); + sleep(1); return 0; } diff --git a/tests/test_splicing.py b/tests/test_splicing.py index 019eaaf29..bfeb46bb1 100644 --- a/tests/test_splicing.py +++ b/tests/test_splicing.py @@ -196,7 +196,7 @@ def test_invalid_splice(node_factory, bitcoind): # The splicing inflight should not have been left pending in the DB assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 0 - l1.daemon.wait_for_log(r'Peer has reconnected, state CHANNELD_NORMAL') + l1.daemon.wait_for_log(r'Restarting channeld after tx_abort on CHANNELD_NORMAL channel') assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 0 @@ -250,7 +250,7 @@ def test_commit_crash_splice(node_factory, bitcoind): # The splicing inflight should have been left pending in the DB assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 1 - l1.daemon.wait_for_log(r'Peer has reconnected, state CHANNELD_NORMAL') + l1.daemon.wait_for_log(r'Restarting channeld after tx_abort on CHANNELD_NORMAL channel') assert l1.db_query("SELECT count(*) as c FROM channel_funding_inflights;")[0]['c'] == 1 diff --git a/tests/test_splicing_disconnect.py b/tests/test_splicing_disconnect.py index 2c854bf5b..f20668498 100644 --- a/tests/test_splicing_disconnect.py +++ b/tests/test_splicing_disconnect.py @@ -100,17 +100,14 @@ def test_splice_disconnect_commit(node_factory, bitcoind, executor): # Should reconnect, and reestablish the splice. l2.start() + # Splice should be abandoned via tx_abort + # Wait until nodes are reconnected 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') - - inv = l2.rpc.invoice(10**2, '3', 'no_3') - l1.rpc.pay(inv['bolt11']) + l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_READY') + l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_READY') # Check that the splice doesn't generate a unilateral close transaction time.sleep(5)