Skip to content

Commit 7165827

Browse files
committed
Send missing splice_locked when confirmation precedes reestablishment
In most cases, we end up sending our `splice_locked` either implicitly during reestablishment via `ChannelReestablish::my_current_funding_locked`, or explicitly after reestablishment. However, we did not consider that it's possible for the node to be notified of the splice confirmation after connecting to their peer but prior to reestablishing their channel. In such cases, we need to explicitly send the `splice_locked` since it wasn't included in `my_current_funding_locked`, but only after the channel has been reestablished. Found by the chanmon_consistency fuzz target.
1 parent 1060865 commit 7165827

3 files changed

Lines changed: 130 additions & 5 deletions

File tree

lightning/src/ln/channel.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,7 @@ pub(super) struct ReestablishResponses {
12651265
pub shutdown_msg: Option<msgs::Shutdown>,
12661266
pub tx_signatures: Option<msgs::TxSignatures>,
12671267
pub tx_abort: Option<msgs::TxAbort>,
1268+
pub splice_locked: Option<msgs::SpliceLocked>,
12681269
pub inferred_splice_locked: Option<msgs::SpliceLocked>,
12691270
}
12701271

@@ -3503,6 +3504,12 @@ pub(super) struct ChannelContext<SP: SignerProvider> {
35033504
/// See-also <https://github.com/lightningnetwork/lnd/issues/4006>
35043505
pub workaround_lnd_bug_4006: Option<msgs::ChannelReady>,
35053506

3507+
/// The `my_current_funding_locked` txid included in our `channel_reestablish` for the current
3508+
/// reconnect, if any. We track this as we cannot tell what was included after we've already
3509+
/// sent it, as it's possible it was unconfirmed at the time we sent it, but confirmed shortly
3510+
/// after.
3511+
funding_locked_txid_sent_in_reestablish: Option<Txid>,
3512+
35063513
/// An option set when we wish to track how many ticks have elapsed while waiting for a response
35073514
/// from our counterparty after entering specific states. If the peer has yet to respond after
35083515
/// reaching `DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`, a reconnection should be attempted to
@@ -4225,6 +4232,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
42254232
announcement_sigs: None,
42264233

42274234
workaround_lnd_bug_4006: None,
4235+
funding_locked_txid_sent_in_reestablish: None,
42284236
sent_message_awaiting_response: None,
42294237

42304238
latest_inbound_scid_alias: None,
@@ -4536,6 +4544,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
45364544
announcement_sigs: None,
45374545

45384546
workaround_lnd_bug_4006: None,
4547+
funding_locked_txid_sent_in_reestablish: None,
45394548
sent_message_awaiting_response: None,
45404549

45414550
latest_inbound_scid_alias: None,
@@ -10512,6 +10521,8 @@ where
1051210521
// remaining cases either succeed or ErrorMessage-fail).
1051310522
self.context.channel_state.clear_peer_disconnected();
1051410523
self.mark_response_received();
10524+
let funding_locked_txid_sent_in_reestablish =
10525+
self.context.funding_locked_txid_sent_in_reestablish.take();
1051510526

1051610527
let shutdown_msg = self.get_outbound_shutdown();
1051710528

@@ -10663,6 +10674,7 @@ where
1066310674
shutdown_msg, announcement_sigs,
1066410675
tx_signatures,
1066510676
tx_abort: None,
10677+
splice_locked: None,
1066610678
inferred_splice_locked: None,
1066710679
});
1066810680
}
@@ -10676,6 +10688,7 @@ where
1067610688
shutdown_msg, announcement_sigs,
1067710689
tx_signatures,
1067810690
tx_abort,
10691+
splice_locked: None,
1067910692
inferred_splice_locked: None,
1068010693
});
1068110694
}
@@ -10745,6 +10758,15 @@ where
1074510758
splice_txid,
1074610759
})
1074710760
});
10761+
let splice_locked = self.pending_splice.as_ref().and_then(|pending_splice| {
10762+
pending_splice
10763+
.sent_funding_txid
10764+
.filter(|splice_txid| Some(*splice_txid) != funding_locked_txid_sent_in_reestablish)
10765+
.map(|splice_txid| msgs::SpliceLocked {
10766+
channel_id: self.context.channel_id,
10767+
splice_txid,
10768+
})
10769+
});
1074810770

1074910771
if msg.next_local_commitment_number == next_counterparty_commitment_number {
1075010772
if required_revoke.is_some() || self.context.signer_pending_revoke_and_ack {
@@ -10763,6 +10785,7 @@ where
1076310785
commitment_order: self.context.resend_order.clone(),
1076410786
tx_signatures,
1076510787
tx_abort,
10788+
splice_locked,
1076610789
inferred_splice_locked,
1076710790
})
1076810791
} else if msg.next_local_commitment_number == next_counterparty_commitment_number - 1 {
@@ -10788,6 +10811,7 @@ where
1078810811
commitment_order: self.context.resend_order.clone(),
1078910812
tx_signatures: None,
1079010813
tx_abort,
10814+
splice_locked,
1079110815
inferred_splice_locked,
1079210816
})
1079310817
} else {
@@ -10815,6 +10839,7 @@ where
1081510839
commitment_order: self.context.resend_order.clone(),
1081610840
tx_signatures: None,
1081710841
tx_abort,
10842+
splice_locked,
1081810843
inferred_splice_locked,
1081910844
})
1082010845
}
@@ -12492,6 +12517,9 @@ where
1249212517
log_info!(logger, "Sending a data_loss_protect with no previous remote per_commitment_secret for channel {}", &self.context.channel_id());
1249312518
[0;32]
1249412519
};
12520+
let my_current_funding_locked = self.maybe_get_my_current_funding_locked();
12521+
self.context.funding_locked_txid_sent_in_reestablish =
12522+
my_current_funding_locked.as_ref().map(|funding_locked| funding_locked.txid);
1249512523
msgs::ChannelReestablish {
1249612524
channel_id: self.context.channel_id(),
1249712525
// The protocol has two different commitment number concepts - the "commitment
@@ -12515,7 +12543,7 @@ where
1251512543
your_last_per_commitment_secret: remote_last_secret,
1251612544
my_current_per_commitment_point: dummy_pubkey,
1251712545
next_funding: self.maybe_get_next_funding(),
12518-
my_current_funding_locked: self.maybe_get_my_current_funding_locked(),
12546+
my_current_funding_locked,
1251912547
}
1252012548
}
1252112549

@@ -17196,6 +17224,7 @@ impl<'a, 'b, 'c, ES: EntropySource, SP: SignerProvider>
1719617224
announcement_sigs,
1719717225

1719817226
workaround_lnd_bug_4006: None,
17227+
funding_locked_txid_sent_in_reestablish: None,
1719917228
sent_message_awaiting_response: None,
1720017229

1720117230
latest_inbound_scid_alias,

lightning/src/ln/channelmanager.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13285,10 +13285,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1328513285
}
1328613286
}
1328713287
let need_lnd_workaround = chan.context.workaround_lnd_bug_4006.take();
13288-
let funding_tx_signed = responses.tx_signatures.map(|tx_signatures| FundingTxSigned {
13289-
tx_signatures: Some(tx_signatures),
13290-
..Default::default()
13291-
});
13288+
let funding_tx_signed = if responses.tx_signatures.is_some() || responses.splice_locked.is_some() {
13289+
Some(FundingTxSigned {
13290+
tx_signatures: responses.tx_signatures,
13291+
splice_locked: responses.splice_locked,
13292+
..Default::default()
13293+
})
13294+
} else {
13295+
None
13296+
};
1329213297
let (htlc_forwards, decode_update_add_htlcs) = self.handle_channel_resumption(
1329313298
&mut peer_state.pending_msg_events, chan, responses.raa, responses.commitment_update, responses.commitment_order,
1329413299
Vec::new(), Vec::new(), None, responses.channel_ready, responses.announcement_sigs,

lightning/src/ln/splicing_tests.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,21 @@ pub fn lock_splice<'a, 'b, 'c, 'd>(
751751
.get_monitor(splice_locked_for_node_b.channel_id)
752752
.map(|monitor| monitor.get_funding_txo().txid)
753753
.unwrap();
754+
complete_splice_locked_exchange(
755+
node_a,
756+
node_b,
757+
splice_locked_for_node_b,
758+
is_0conf,
759+
expected_discard_txids,
760+
prev_funding_txid,
761+
)
762+
}
754763

764+
fn complete_splice_locked_exchange<'a, 'b, 'c, 'd>(
765+
node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>,
766+
splice_locked_for_node_b: &msgs::SpliceLocked, is_0conf: bool, expected_discard_txids: &[Txid],
767+
prev_funding_txid: Txid,
768+
) -> SpliceLockedResult {
755769
let node_id_a = node_a.node.get_our_node_id();
756770
let node_id_b = node_b.node.get_our_node_id();
757771

@@ -2585,6 +2599,83 @@ fn do_test_splice_reestablish(reload: bool, async_monitor_update: bool) {
25852599
.remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script);
25862600
}
25872601

2602+
#[test]
2603+
fn test_splice_locked_waits_for_channel_reestablish() {
2604+
// If a splice confirms after `peer_connected` but before `channel_reestablish` is handled, the
2605+
// peer state is connected while the channel still has its disconnected bit set. We must not send
2606+
// `splice_locked` until the channel is reestablished, but should send it immediately after.
2607+
let chanmon_cfgs = create_chanmon_cfgs(2);
2608+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
2609+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
2610+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
2611+
2612+
let node_id_0 = nodes[0].node.get_our_node_id();
2613+
let node_id_1 = nodes[1].node.get_our_node_id();
2614+
2615+
let initial_channel_value_sat = 100_000;
2616+
let (_, _, channel_id, _) =
2617+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_value_sat, 0);
2618+
let prev_funding_txid = get_monitor!(nodes[0], channel_id).get_funding_txo().txid;
2619+
2620+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
2621+
2622+
let outputs = vec![
2623+
TxOut {
2624+
value: Amount::from_sat(initial_channel_value_sat / 4),
2625+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
2626+
},
2627+
TxOut {
2628+
value: Amount::from_sat(initial_channel_value_sat / 4),
2629+
script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(),
2630+
},
2631+
];
2632+
let funding_contribution =
2633+
initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).unwrap();
2634+
let (splice_tx, _) = splice_channel(&nodes[0], &nodes[1], channel_id, funding_contribution);
2635+
2636+
nodes[0].node.peer_disconnected(node_id_1);
2637+
nodes[1].node.peer_disconnected(node_id_0);
2638+
2639+
connect_nodes(&nodes[0], &nodes[1]);
2640+
let reestablish_0 =
2641+
get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, node_id_1);
2642+
let reestablish_1 =
2643+
get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, node_id_0);
2644+
2645+
confirm_transaction(&nodes[0], &splice_tx);
2646+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
2647+
2648+
nodes[1].node.handle_channel_reestablish(node_id_0, &reestablish_0);
2649+
let _ = get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, node_id_0);
2650+
nodes[0].node.handle_channel_reestablish(node_id_1, &reestablish_1);
2651+
let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
2652+
assert_eq!(msg_events.len(), 2, "{msg_events:?}");
2653+
let splice_locked_0 =
2654+
if let MessageSendEvent::SendSpliceLocked { node_id, msg } = msg_events.remove(0) {
2655+
assert_eq!(node_id, node_id_1);
2656+
msg
2657+
} else {
2658+
panic!();
2659+
};
2660+
if let MessageSendEvent::SendChannelUpdate { node_id, .. } = msg_events.remove(0) {
2661+
assert_eq!(node_id, node_id_1);
2662+
} else {
2663+
panic!();
2664+
}
2665+
2666+
confirm_transaction(&nodes[1], &splice_tx);
2667+
complete_splice_locked_exchange(
2668+
&nodes[0],
2669+
&nodes[1],
2670+
&splice_locked_0,
2671+
false,
2672+
&[],
2673+
prev_funding_txid,
2674+
);
2675+
2676+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
2677+
}
2678+
25882679
#[test]
25892680
fn test_splice_confirms_on_both_sides_while_disconnected() {
25902681
// Regression test: when a splice transaction confirms on both sides while peers are

0 commit comments

Comments
 (0)