Skip to content

Commit f7d6db1

Browse files
committed
Push splice initiation through the quiescent pipeline
Now that we have a `QuiescentAction` to track what we intend to do once we reach quiescence, we need to use it to initiate splices. Here we do so, adding a new `SpliceInstructions` to track the arguments that are currently passed to `splice_channel`. While these may not be exactly the right arguments to track in the end, a lot of the splice logic is still in flight, so we can worry about it later.
1 parent 23e8a55 commit f7d6db1

File tree

3 files changed

+147
-56
lines changed

3 files changed

+147
-56
lines changed

lightning/src/ln/channel.rs

Lines changed: 106 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2443,13 +2443,42 @@ impl PendingSplice {
24432443
}
24442444
}
24452445

2446+
pub(crate) struct SpliceInstructions {
2447+
our_funding_contribution_satoshis: i64,
2448+
our_funding_inputs: Vec<(TxIn, Transaction, Weight)>,
2449+
change_script: Option<ScriptBuf>,
2450+
funding_feerate_per_kw: u32,
2451+
locktime: u32,
2452+
}
2453+
2454+
impl_writeable_tlv_based!(SpliceInstructions, {
2455+
(1, our_funding_contribution_satoshis, required),
2456+
(3, our_funding_inputs, required_vec),
2457+
(5, change_script, option),
2458+
(7, funding_feerate_per_kw, required),
2459+
(9, locktime, required),
2460+
});
2461+
24462462
pub(crate) enum QuiescentAction {
2447-
// TODO: Make this test-only once we have another variant (as some code requires *a* variant).
2463+
Splice(SpliceInstructions),
2464+
#[cfg(any(test, fuzzing))]
24482465
DoNothing,
24492466
}
24502467

2468+
pub(crate) enum StfuResponse {
2469+
Stfu(msgs::Stfu),
2470+
#[cfg_attr(not(splicing), allow(unused))]
2471+
SpliceInit(msgs::SpliceInit),
2472+
}
2473+
2474+
#[cfg(any(test, fuzzing))]
24512475
impl_writeable_tlv_based_enum_upgradable!(QuiescentAction,
2452-
(99, DoNothing) => {},
2476+
(0, DoNothing) => {},
2477+
{1, Splice} => (),
2478+
);
2479+
#[cfg(not(any(test, fuzzing)))]
2480+
impl_writeable_tlv_based_enum_upgradable!(QuiescentAction,,
2481+
{1, Splice} => (),
24532482
);
24542483

24552484
/// Wrapper around a [`Transaction`] useful for caching the result of [`Transaction::compute_txid`].
@@ -5926,7 +5955,7 @@ fn estimate_v2_funding_transaction_fee(
59265955
fn check_v2_funding_inputs_sufficient(
59275956
contribution_amount: i64, funding_inputs: &[(TxIn, Transaction, Weight)], is_initiator: bool,
59285957
is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
5929-
) -> Result<u64, ChannelError> {
5958+
) -> Result<u64, String> {
59305959
let mut total_input_witness_weight = Weight::from_wu(funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum());
59315960
let mut funding_inputs_len = funding_inputs.len();
59325961
if is_initiator && is_splice {
@@ -5941,10 +5970,10 @@ fn check_v2_funding_inputs_sufficient(
59415970
if let Some(output) = input.1.output.get(input.0.previous_output.vout as usize) {
59425971
total_input_sats = total_input_sats.saturating_add(output.value.to_sat());
59435972
} else {
5944-
return Err(ChannelError::Warn(format!(
5973+
return Err(format!(
59455974
"Transaction with txid {} does not have an output with vout of {} corresponding to TxIn at funding_inputs[{}]",
59465975
input.1.compute_txid(), input.0.previous_output.vout, idx
5947-
)));
5976+
));
59485977
}
59495978
}
59505979

@@ -5961,10 +5990,10 @@ fn check_v2_funding_inputs_sufficient(
59615990

59625991
let minimal_input_amount_needed = contribution_amount.saturating_add(estimated_fee as i64);
59635992
if (total_input_sats as i64) < minimal_input_amount_needed {
5964-
Err(ChannelError::Warn(format!(
5993+
Err(format!(
59655994
"Total input amount {} is lower than needed for contribution {}, considering fees of {}. Need more inputs.",
59665995
total_input_sats, contribution_amount, estimated_fee,
5967-
)))
5996+
))
59685997
} else {
59695998
Ok(estimated_fee)
59705999
}
@@ -10614,11 +10643,14 @@ where
1061410643
/// - `change_script`: an option change output script. If `None` and needed, one will be
1061510644
/// generated by `SignerProvider::get_destination_script`.
1061610645
#[cfg(splicing)]
10617-
pub fn splice_channel(
10646+
pub fn splice_channel<L: Deref>(
1061810647
&mut self, our_funding_contribution_satoshis: i64,
1061910648
our_funding_inputs: Vec<(TxIn, Transaction, Weight)>, change_script: Option<ScriptBuf>,
10620-
funding_feerate_per_kw: u32, locktime: u32,
10621-
) -> Result<msgs::SpliceInit, APIError> {
10649+
funding_feerate_per_kw: u32, locktime: u32, logger: &L,
10650+
) -> Result<Option<msgs::Stfu>, APIError>
10651+
where
10652+
L::Target: Logger,
10653+
{
1062210654
// Check if a splice has been initiated already.
1062310655
// Note: only a single outstanding splice is supported (per spec)
1062410656
if self.pending_splice.is_some() {
@@ -10671,11 +10703,44 @@ where
1067110703
err,
1067210704
),
1067310705
})?;
10674-
// Convert inputs
10675-
let mut funding_inputs = Vec::new();
10676-
for (tx_in, tx, _w) in our_funding_inputs.into_iter() {
10677-
let tx16 = TransactionU16LenLimited::new(tx)
10678-
.map_err(|_e| APIError::APIMisuseError { err: format!("Too large transaction") })?;
10706+
10707+
// TODO(splicing): Check that transactions aren't too big for the splice_init message here.
10708+
10709+
let action = QuiescentAction::Splice(SpliceInstructions {
10710+
our_funding_contribution_satoshis,
10711+
our_funding_inputs,
10712+
change_script,
10713+
funding_feerate_per_kw,
10714+
locktime,
10715+
});
10716+
self.propose_quiescence(logger, action)
10717+
.map_err(|e| APIError::APIMisuseError { err: e.to_owned() })
10718+
}
10719+
10720+
#[cfg(splicing)]
10721+
fn send_splice_init(
10722+
&mut self, instructions: SpliceInstructions,
10723+
) -> Result<msgs::SpliceInit, String> {
10724+
let SpliceInstructions {
10725+
our_funding_contribution_satoshis,
10726+
our_funding_inputs,
10727+
change_script,
10728+
funding_feerate_per_kw,
10729+
locktime,
10730+
} = instructions;
10731+
10732+
// Check that the channel value hasn't changed out from under us.
10733+
let _fee = check_v2_funding_inputs_sufficient(
10734+
our_funding_contribution_satoshis,
10735+
&our_funding_inputs,
10736+
true,
10737+
true,
10738+
funding_feerate_per_kw,
10739+
)?;
10740+
10741+
let mut funding_inputs = Vec::with_capacity(our_funding_inputs.len());
10742+
for (tx_in, tx, _weight) in our_funding_inputs {
10743+
let tx16 = TransactionU16LenLimited::new(tx).map_err(|_e| "tx too big".to_owned())?;
1067910744
funding_inputs.push((tx_in, tx16));
1068010745
}
1068110746

@@ -11567,23 +11632,21 @@ where
1156711632
);
1156811633
}
1156911634

11570-
#[cfg(any(test, fuzzing))]
11635+
#[cfg(any(splicing, test, fuzzing))]
1157111636
#[rustfmt::skip]
1157211637
pub fn propose_quiescence<L: Deref>(
1157311638
&mut self, logger: &L, action: QuiescentAction,
11574-
) -> Result<Option<msgs::Stfu>, ChannelError>
11639+
) -> Result<Option<msgs::Stfu>, &'static str>
1157511640
where
1157611641
L::Target: Logger,
1157711642
{
1157811643
log_debug!(logger, "Attempting to initiate quiescence");
1157911644

1158011645
if !self.context.is_usable() {
11581-
return Err(ChannelError::Ignore(
11582-
"Channel is not in a usable state to propose quiescence".to_owned()
11583-
));
11646+
return Err("Channel is not in a usable state to propose quiescence");
1158411647
}
1158511648
if self.context.post_quiescence_action.is_some() {
11586-
return Err(ChannelError::Ignore("Channel is already quiescing".to_owned()));
11649+
return Err("Channel is already quiescing");
1158711650
}
1158811651

1158911652
self.context.post_quiescence_action = Some(action);
@@ -11604,7 +11667,7 @@ where
1160411667

1160511668
// Assumes we are either awaiting quiescence or our counterparty has requested quiescence.
1160611669
#[rustfmt::skip]
11607-
pub fn send_stfu<L: Deref>(&mut self, logger: &L) -> Result<msgs::Stfu, ChannelError>
11670+
pub fn send_stfu<L: Deref>(&mut self, logger: &L) -> Result<msgs::Stfu, &'static str>
1160811671
where
1160911672
L::Target: Logger,
1161011673
{
@@ -11618,9 +11681,7 @@ where
1161811681
if self.context.is_waiting_on_peer_pending_channel_update()
1161911682
|| self.context.is_monitor_or_signer_pending_channel_update()
1162011683
{
11621-
return Err(ChannelError::Ignore(
11622-
"We cannot send `stfu` while state machine is pending".to_owned()
11623-
));
11684+
return Err("We cannot send `stfu` while state machine is pending")
1162411685
}
1162511686

1162611687
let initiator = if self.context.channel_state.is_remote_stfu_sent() {
@@ -11646,7 +11707,7 @@ where
1164611707
#[rustfmt::skip]
1164711708
pub fn stfu<L: Deref>(
1164811709
&mut self, msg: &msgs::Stfu, logger: &L
11649-
) -> Result<Option<msgs::Stfu>, ChannelError> where L::Target: Logger {
11710+
) -> Result<Option<StfuResponse>, ChannelError> where L::Target: Logger {
1165011711
if self.context.channel_state.is_quiescent() {
1165111712
return Err(ChannelError::Warn("Channel is already quiescent".to_owned()));
1165211713
}
@@ -11677,7 +11738,10 @@ where
1167711738
self.context.channel_state.set_remote_stfu_sent();
1167811739

1167911740
log_debug!(logger, "Received counterparty stfu proposing quiescence");
11680-
return self.send_stfu(logger).map(|stfu| Some(stfu));
11741+
return self
11742+
.send_stfu(logger)
11743+
.map(|stfu| Some(StfuResponse::Stfu(stfu)))
11744+
.map_err(|e| ChannelError::Ignore(e.to_owned()));
1168111745
}
1168211746

1168311747
// We already sent `stfu` and are now processing theirs. It may be in response to ours, or
@@ -11718,6 +11782,13 @@ where
1171811782
"Internal Error: Didn't have anything to do after reaching quiescence".to_owned()
1171911783
));
1172011784
},
11785+
Some(QuiescentAction::Splice(_instructions)) => {
11786+
#[cfg(splicing)]
11787+
return self.send_splice_init(_instructions)
11788+
.map(|splice_init| Some(StfuResponse::SpliceInit(splice_init)))
11789+
.map_err(|e| ChannelError::Ignore(e.to_owned()));
11790+
},
11791+
#[cfg(any(test, fuzzing))]
1172111792
Some(QuiescentAction::DoNothing) => {
1172211793
// In quiescence test we want to just hang out here, letting the test manually
1172311794
// leave quiescence.
@@ -11750,7 +11821,10 @@ where
1175011821
|| (self.context.channel_state.is_remote_stfu_sent()
1175111822
&& !self.context.channel_state.is_local_stfu_sent())
1175211823
{
11753-
return self.send_stfu(logger).map(|stfu| Some(stfu));
11824+
return self
11825+
.send_stfu(logger)
11826+
.map(|stfu| Some(stfu))
11827+
.map_err(|e| ChannelError::Ignore(e.to_owned()));
1175411828
}
1175511829

1175611830
// We're either:
@@ -15920,8 +15994,8 @@ mod tests {
1592015994
2000,
1592115995
);
1592215996
assert_eq!(
15923-
format!("{:?}", res.err().unwrap()),
15924-
"Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1730. Need more inputs.",
15997+
res.err().unwrap(),
15998+
"Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1730. Need more inputs.",
1592515999
);
1592616000
}
1592716001

@@ -15956,8 +16030,8 @@ mod tests {
1595616030
2200,
1595716031
);
1595816032
assert_eq!(
15959-
format!("{:?}", res.err().unwrap()),
15960-
"Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2495. Need more inputs.",
16033+
res.err().unwrap(),
16034+
"Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2495. Need more inputs.",
1596116035
);
1596216036
}
1596316037

lightning/src/ln/channelmanager.rs

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use crate::ln::channel::QuiescentAction;
6262
use crate::ln::channel::{
6363
self, hold_time_since, Channel, ChannelError, ChannelUpdateStatus, FundedChannel,
6464
InboundV1Channel, OutboundV1Channel, PendingV2Channel, ReconnectionMsg, ShutdownResult,
65-
UpdateFulfillCommitFetch, WithChannelContext,
65+
StfuResponse, UpdateFulfillCommitFetch, WithChannelContext,
6666
};
6767
use crate::ln::channel_state::ChannelDetails;
6868
use crate::ln::inbound_payment;
@@ -4503,17 +4503,21 @@ where
45034503
hash_map::Entry::Occupied(mut chan_phase_entry) => {
45044504
let locktime = locktime.unwrap_or_else(|| self.current_best_block().height);
45054505
if let Some(chan) = chan_phase_entry.get_mut().as_funded_mut() {
4506-
let msg = chan.splice_channel(
4506+
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
4507+
let msg_opt = chan.splice_channel(
45074508
our_funding_contribution_satoshis,
45084509
our_funding_inputs,
45094510
change_script,
45104511
funding_feerate_per_kw,
45114512
locktime,
4513+
&&logger,
45124514
)?;
4513-
peer_state.pending_msg_events.push(MessageSendEvent::SendSpliceInit {
4514-
node_id: *counterparty_node_id,
4515-
msg,
4516-
});
4515+
if let Some(msg) = msg_opt {
4516+
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
4517+
node_id: *counterparty_node_id,
4518+
msg,
4519+
});
4520+
}
45174521
Ok(())
45184522
} else {
45194523
Err(APIError::ChannelUnavailable {
@@ -10841,22 +10845,31 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1084110845
));
1084210846
}
1084310847

10844-
let mut sent_stfu = false;
1084510848
match peer_state.channel_by_id.entry(msg.channel_id) {
1084610849
hash_map::Entry::Occupied(mut chan_entry) => {
1084710850
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
1084810851
let logger = WithContext::from(
1084910852
&self.logger, Some(*counterparty_node_id), Some(msg.channel_id), None
1085010853
);
1085110854

10852-
if let Some(stfu) = try_channel_entry!(
10853-
self, peer_state, chan.stfu(&msg, &&logger), chan_entry
10854-
) {
10855-
sent_stfu = true;
10856-
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
10857-
node_id: *counterparty_node_id,
10858-
msg: stfu,
10859-
});
10855+
let res = chan.stfu(&msg, &&logger);
10856+
let resp = try_channel_entry!(self, peer_state, res, chan_entry);
10857+
match resp {
10858+
None => Ok(false),
10859+
Some(StfuResponse::Stfu(msg)) => {
10860+
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
10861+
node_id: *counterparty_node_id,
10862+
msg,
10863+
});
10864+
Ok(true)
10865+
},
10866+
Some(StfuResponse::SpliceInit(msg)) => {
10867+
peer_state.pending_msg_events.push(MessageSendEvent::SendSpliceInit {
10868+
node_id: *counterparty_node_id,
10869+
msg,
10870+
});
10871+
Ok(true)
10872+
},
1086010873
}
1086110874
} else {
1086210875
let msg = "Peer sent `stfu` for an unfunded channel";
@@ -10871,8 +10884,6 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1087110884
msg.channel_id
1087210885
))
1087310886
}
10874-
10875-
Ok(sent_stfu)
1087610887
}
1087710888

1087810889
#[rustfmt::skip]
@@ -13884,8 +13895,8 @@ where
1388413895
let persist = match &res {
1388513896
Err(e) if e.closes_channel() => NotifyOption::DoPersist,
1388613897
Err(_) => NotifyOption::SkipPersistHandleEvents,
13887-
Ok(sent_stfu) => {
13888-
if *sent_stfu {
13898+
Ok(responded) => {
13899+
if *responded {
1388913900
NotifyOption::SkipPersistHandleEvents
1389013901
} else {
1389113902
NotifyOption::SkipPersistNoEvents

lightning/src/ln/splicing_tests.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ fn test_v1_splice_in() {
2525
let acceptor_node_index = 1;
2626
let initiator_node = &nodes[initiator_node_index];
2727
let acceptor_node = &nodes[acceptor_node_index];
28+
let initiator_node_id = initiator_node.node.get_our_node_id();
29+
let acceptor_node_id = acceptor_node.node.get_our_node_id();
2830

2931
let channel_value_sat = 100_000;
3032
let channel_reserve_amnt_sat = 1_000;
@@ -79,12 +81,16 @@ fn test_v1_splice_in() {
7981
None, // locktime
8082
)
8183
.unwrap();
84+
85+
let init_stfu = get_event_msg!(initiator_node, MessageSendEvent::SendStfu, acceptor_node_id);
86+
acceptor_node.node.handle_stfu(initiator_node_id, &init_stfu);
87+
88+
let ack_stfu = get_event_msg!(acceptor_node, MessageSendEvent::SendStfu, initiator_node_id);
89+
initiator_node.node.handle_stfu(acceptor_node_id, &ack_stfu);
90+
8291
// Extract the splice_init message
83-
let splice_init_msg = get_event_msg!(
84-
initiator_node,
85-
MessageSendEvent::SendSpliceInit,
86-
acceptor_node.node.get_our_node_id()
87-
);
92+
let splice_init_msg =
93+
get_event_msg!(initiator_node, MessageSendEvent::SendSpliceInit, acceptor_node_id);
8894
assert_eq!(splice_init_msg.funding_contribution_satoshis, splice_in_sats as i64);
8995
assert_eq!(splice_init_msg.funding_feerate_per_kw, funding_feerate_per_kw);
9096
assert_eq!(splice_init_msg.funding_pubkey.to_string(), expected_initiator_funding_key);

0 commit comments

Comments
 (0)