Skip to content

Commit 92f1961

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 2a6501a commit 92f1961

File tree

6 files changed

+180
-48
lines changed

6 files changed

+180
-48
lines changed

lightning/src/events/bump_transaction/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ pub struct Utxo {
264264
pub satisfaction_weight: u64,
265265
}
266266

267+
impl_writeable_tlv_based!(Utxo, {
268+
(1, outpoint, required),
269+
(3, output, required),
270+
(5, satisfaction_weight, required),
271+
});
272+
267273
impl Utxo {
268274
/// Returns a `Utxo` with the `satisfaction_weight` estimate for a legacy P2PKH output.
269275
pub fn new_p2pkh(outpoint: OutPoint, value: Amount, pubkey_hash: &PubkeyHash) -> Self {

lightning/src/ln/channel.rs

Lines changed: 107 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,13 +2448,46 @@ impl PendingSplice {
24482448
}
24492449
}
24502450

2451+
pub(crate) struct SpliceInstructions {
2452+
adjusted_funding_contribution: SignedAmount,
2453+
our_funding_inputs: Vec<FundingTxInput>,
2454+
our_funding_outputs: Vec<TxOut>,
2455+
change_script: Option<ScriptBuf>,
2456+
funding_feerate_per_kw: u32,
2457+
locktime: u32,
2458+
original_funding_txo: OutPoint,
2459+
}
2460+
2461+
impl_writeable_tlv_based!(SpliceInstructions, {
2462+
(1, adjusted_funding_contribution, required),
2463+
(3, our_funding_inputs, required_vec),
2464+
(5, our_funding_outputs, required_vec),
2465+
(7, change_script, option),
2466+
(9, funding_feerate_per_kw, required),
2467+
(11, locktime, required),
2468+
(13, original_funding_txo, required),
2469+
});
2470+
24512471
pub(crate) enum QuiescentAction {
2452-
// TODO: Make this test-only once we have another variant (as some code requires *a* variant).
2472+
Splice(SpliceInstructions),
2473+
#[cfg(any(test, fuzzing))]
24532474
DoNothing,
24542475
}
24552476

2477+
pub(crate) enum StfuResponse {
2478+
Stfu(msgs::Stfu),
2479+
#[cfg_attr(not(splicing), allow(unused))]
2480+
SpliceInit(msgs::SpliceInit),
2481+
}
2482+
2483+
#[cfg(any(test, fuzzing))]
24562484
impl_writeable_tlv_based_enum_upgradable!(QuiescentAction,
2457-
(99, DoNothing) => {},
2485+
(0, DoNothing) => {},
2486+
{1, Splice} => (),
2487+
);
2488+
#[cfg(not(any(test, fuzzing)))]
2489+
impl_writeable_tlv_based_enum_upgradable!(QuiescentAction,,
2490+
{1, Splice} => (),
24582491
);
24592492

24602493
/// Wrapper around a [`Transaction`] useful for caching the result of [`Transaction::compute_txid`].
@@ -10748,9 +10781,13 @@ where
1074810781
/// - `change_script`: an option change output script. If `None` and needed, one will be
1074910782
/// generated by `SignerProvider::get_destination_script`.
1075010783
#[cfg(splicing)]
10751-
pub fn splice_channel(
10784+
pub fn splice_channel<L: Deref>(
1075210785
&mut self, contribution: SpliceContribution, funding_feerate_per_kw: u32, locktime: u32,
10753-
) -> Result<msgs::SpliceInit, APIError> {
10786+
logger: &L,
10787+
) -> Result<Option<msgs::Stfu>, APIError>
10788+
where
10789+
L::Target: Logger,
10790+
{
1075410791
if self.holder_commitment_point.current_point().is_none() {
1075510792
return Err(APIError::APIMisuseError {
1075610793
err: format!(
@@ -10762,7 +10799,7 @@ where
1076210799

1076310800
// Check if a splice has been initiated already.
1076410801
// Note: only a single outstanding splice is supported (per spec)
10765-
if self.pending_splice.is_some() {
10802+
if self.pending_splice.is_some() || self.quiescent_action.is_some() {
1076610803
return Err(APIError::APIMisuseError {
1076710804
err: format!(
1076810805
"Channel {} cannot be spliced, as it has already a splice pending",
@@ -10780,8 +10817,6 @@ where
1078010817
});
1078110818
}
1078210819

10783-
// TODO(splicing): check for quiescence
10784-
1078510820
let our_funding_contribution = contribution.value();
1078610821
if our_funding_contribution == SignedAmount::ZERO {
1078710822
return Err(APIError::APIMisuseError {
@@ -10876,8 +10911,50 @@ where
1087610911
}
1087710912
}
1087810913

10879-
let prev_funding_input = self.funding.to_splice_funding_input();
10914+
let original_funding_txo = self.funding.get_funding_txo().ok_or_else(|| {
10915+
debug_assert!(false);
10916+
APIError::APIMisuseError { err: "Channel isn't yet fully funded".to_owned() }
10917+
})?;
10918+
1088010919
let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
10920+
10921+
let action = QuiescentAction::Splice(SpliceInstructions {
10922+
adjusted_funding_contribution,
10923+
our_funding_inputs,
10924+
our_funding_outputs,
10925+
change_script,
10926+
funding_feerate_per_kw,
10927+
locktime,
10928+
original_funding_txo,
10929+
});
10930+
self.propose_quiescence(logger, action)
10931+
.map_err(|e| APIError::APIMisuseError { err: e.to_owned() })
10932+
}
10933+
10934+
#[cfg(splicing)]
10935+
fn send_splice_init(
10936+
&mut self, instructions: SpliceInstructions,
10937+
) -> Result<msgs::SpliceInit, String> {
10938+
let SpliceInstructions {
10939+
adjusted_funding_contribution,
10940+
our_funding_inputs,
10941+
our_funding_outputs,
10942+
change_script,
10943+
funding_feerate_per_kw,
10944+
locktime,
10945+
original_funding_txo,
10946+
} = instructions;
10947+
10948+
// Check if a splice has been initiated already.
10949+
// Note: only a single outstanding splice is supported (per spec)
10950+
if self.pending_splice.is_some() {
10951+
return Err(format!(
10952+
"Channel {} cannot be spliced, as it has already a splice pending",
10953+
self.context.channel_id(),
10954+
));
10955+
}
10956+
10957+
let prev_funding_input = self.funding.to_splice_funding_input();
1088110958
let funding_negotiation_context = FundingNegotiationContext {
1088210959
is_initiator: true,
1088310960
our_funding_contribution: adjusted_funding_contribution,
@@ -11820,23 +11897,21 @@ where
1182011897
);
1182111898
}
1182211899

11823-
#[cfg(any(test, fuzzing))]
11900+
#[cfg(any(splicing, test, fuzzing))]
1182411901
#[rustfmt::skip]
1182511902
pub fn propose_quiescence<L: Deref>(
1182611903
&mut self, logger: &L, action: QuiescentAction,
11827-
) -> Result<Option<msgs::Stfu>, ChannelError>
11904+
) -> Result<Option<msgs::Stfu>, &'static str>
1182811905
where
1182911906
L::Target: Logger,
1183011907
{
1183111908
log_debug!(logger, "Attempting to initiate quiescence");
1183211909

1183311910
if !self.context.is_usable() {
11834-
return Err(ChannelError::Ignore(
11835-
"Channel is not in a usable state to propose quiescence".to_owned()
11836-
));
11911+
return Err("Channel is not in a usable state to propose quiescence");
1183711912
}
1183811913
if self.quiescent_action.is_some() {
11839-
return Err(ChannelError::Ignore("Channel is already quiescing".to_owned()));
11914+
return Err("Channel already has a pending quiescent action and cannot start another");
1184011915
}
1184111916

1184211917
self.quiescent_action = Some(action);
@@ -11857,7 +11932,7 @@ where
1185711932

1185811933
// Assumes we are either awaiting quiescence or our counterparty has requested quiescence.
1185911934
#[rustfmt::skip]
11860-
pub fn send_stfu<L: Deref>(&mut self, logger: &L) -> Result<msgs::Stfu, ChannelError>
11935+
pub fn send_stfu<L: Deref>(&mut self, logger: &L) -> Result<msgs::Stfu, &'static str>
1186111936
where
1186211937
L::Target: Logger,
1186311938
{
@@ -11871,9 +11946,7 @@ where
1187111946
if self.context.is_waiting_on_peer_pending_channel_update()
1187211947
|| self.context.is_monitor_or_signer_pending_channel_update()
1187311948
{
11874-
return Err(ChannelError::Ignore(
11875-
"We cannot send `stfu` while state machine is pending".to_owned()
11876-
));
11949+
return Err("We cannot send `stfu` while state machine is pending")
1187711950
}
1187811951

1187911952
let initiator = if self.context.channel_state.is_remote_stfu_sent() {
@@ -11899,7 +11972,7 @@ where
1189911972
#[rustfmt::skip]
1190011973
pub fn stfu<L: Deref>(
1190111974
&mut self, msg: &msgs::Stfu, logger: &L
11902-
) -> Result<Option<msgs::Stfu>, ChannelError> where L::Target: Logger {
11975+
) -> Result<Option<StfuResponse>, ChannelError> where L::Target: Logger {
1190311976
if self.context.channel_state.is_quiescent() {
1190411977
return Err(ChannelError::Warn("Channel is already quiescent".to_owned()));
1190511978
}
@@ -11930,7 +12003,10 @@ where
1193012003
self.context.channel_state.set_remote_stfu_sent();
1193112004

1193212005
log_debug!(logger, "Received counterparty stfu proposing quiescence");
11933-
return self.send_stfu(logger).map(|stfu| Some(stfu));
12006+
return self
12007+
.send_stfu(logger)
12008+
.map(|stfu| Some(StfuResponse::Stfu(stfu)))
12009+
.map_err(|e| ChannelError::Ignore(e.to_owned()));
1193412010
}
1193512011

1193612012
// We already sent `stfu` and are now processing theirs. It may be in response to ours, or
@@ -11971,6 +12047,13 @@ where
1197112047
"Internal Error: Didn't have anything to do after reaching quiescence".to_owned()
1197212048
));
1197312049
},
12050+
Some(QuiescentAction::Splice(_instructions)) => {
12051+
#[cfg(splicing)]
12052+
return self.send_splice_init(_instructions)
12053+
.map(|splice_init| Some(StfuResponse::SpliceInit(splice_init)))
12054+
.map_err(|e| ChannelError::WarnAndDisconnect(e.to_owned()));
12055+
},
12056+
#[cfg(any(test, fuzzing))]
1197412057
Some(QuiescentAction::DoNothing) => {
1197512058
// In quiescence test we want to just hang out here, letting the test manually
1197612059
// leave quiescence.
@@ -12003,7 +12086,10 @@ where
1200312086
|| (self.context.channel_state.is_remote_stfu_sent()
1200412087
&& !self.context.channel_state.is_local_stfu_sent())
1200512088
{
12006-
return self.send_stfu(logger).map(|stfu| Some(stfu));
12089+
return self
12090+
.send_stfu(logger)
12091+
.map(|stfu| Some(stfu))
12092+
.map_err(|e| ChannelError::Ignore(e.to_owned()));
1200712093
}
1200812094

1200912095
// We're either:

lightning/src/ln/channelmanager.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ use crate::ln::channel::QuiescentAction;
6161
use crate::ln::channel::{
6262
self, hold_time_since, Channel, ChannelError, ChannelUpdateStatus, FundedChannel,
6363
InboundV1Channel, OutboundV1Channel, PendingV2Channel, ReconnectionMsg, ShutdownResult,
64-
UpdateFulfillCommitFetch, WithChannelContext,
64+
StfuResponse, UpdateFulfillCommitFetch, WithChannelContext,
6565
};
6666
use crate::ln::channel_state::ChannelDetails;
6767
#[cfg(splicing)]
@@ -4494,12 +4494,19 @@ where
44944494
hash_map::Entry::Occupied(mut chan_phase_entry) => {
44954495
let locktime = locktime.unwrap_or_else(|| self.current_best_block().height);
44964496
if let Some(chan) = chan_phase_entry.get_mut().as_funded_mut() {
4497-
let msg =
4498-
chan.splice_channel(contribution, funding_feerate_per_kw, locktime)?;
4499-
peer_state.pending_msg_events.push(MessageSendEvent::SendSpliceInit {
4500-
node_id: *counterparty_node_id,
4501-
msg,
4502-
});
4497+
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
4498+
let msg_opt = chan.splice_channel(
4499+
contribution,
4500+
funding_feerate_per_kw,
4501+
locktime,
4502+
&&logger,
4503+
)?;
4504+
if let Some(msg) = msg_opt {
4505+
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
4506+
node_id: *counterparty_node_id,
4507+
msg,
4508+
});
4509+
}
45034510
Ok(())
45044511
} else {
45054512
Err(APIError::ChannelUnavailable {
@@ -10875,22 +10882,31 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1087510882
));
1087610883
}
1087710884

10878-
let mut sent_stfu = false;
1087910885
match peer_state.channel_by_id.entry(msg.channel_id) {
1088010886
hash_map::Entry::Occupied(mut chan_entry) => {
1088110887
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
1088210888
let logger = WithContext::from(
1088310889
&self.logger, Some(*counterparty_node_id), Some(msg.channel_id), None
1088410890
);
1088510891

10886-
if let Some(stfu) = try_channel_entry!(
10887-
self, peer_state, chan.stfu(&msg, &&logger), chan_entry
10888-
) {
10889-
sent_stfu = true;
10890-
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
10891-
node_id: *counterparty_node_id,
10892-
msg: stfu,
10893-
});
10892+
let res = chan.stfu(&msg, &&logger);
10893+
let resp = try_channel_entry!(self, peer_state, res, chan_entry);
10894+
match resp {
10895+
None => Ok(false),
10896+
Some(StfuResponse::Stfu(msg)) => {
10897+
peer_state.pending_msg_events.push(MessageSendEvent::SendStfu {
10898+
node_id: *counterparty_node_id,
10899+
msg,
10900+
});
10901+
Ok(true)
10902+
},
10903+
Some(StfuResponse::SpliceInit(msg)) => {
10904+
peer_state.pending_msg_events.push(MessageSendEvent::SendSpliceInit {
10905+
node_id: *counterparty_node_id,
10906+
msg,
10907+
});
10908+
Ok(true)
10909+
},
1089410910
}
1089510911
} else {
1089610912
let msg = "Peer sent `stfu` for an unfunded channel";
@@ -10905,8 +10921,6 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1090510921
msg.channel_id
1090610922
))
1090710923
}
10908-
10909-
Ok(sent_stfu)
1091010924
}
1091110925

1091210926
#[rustfmt::skip]
@@ -13873,8 +13887,8 @@ where
1387313887
let persist = match &res {
1387413888
Err(e) if e.closes_channel() => NotifyOption::DoPersist,
1387513889
Err(_) => NotifyOption::SkipPersistHandleEvents,
13876-
Ok(sent_stfu) => {
13877-
if *sent_stfu {
13890+
Ok(responded) => {
13891+
if *responded {
1387813892
NotifyOption::SkipPersistHandleEvents
1387913893
} else {
1388013894
NotifyOption::SkipPersistNoEvents

lightning/src/ln/funding.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ pub struct FundingTxInput {
104104
pub(super) prevtx: Transaction,
105105
}
106106

107+
impl_writeable_tlv_based!(FundingTxInput, {
108+
(1, utxo, required),
109+
(3, sequence, required),
110+
(5, prevtx, required),
111+
});
112+
107113
impl FundingTxInput {
108114
fn new<F: FnOnce(&bitcoin::Script) -> bool>(
109115
prevtx: Transaction, vout: u32, witness_weight: Weight, script_filter: F,

lightning/src/ln/splicing_tests.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ fn test_v1_splice_in() {
2828
let acceptor_node_index = 1;
2929
let initiator_node = &nodes[initiator_node_index];
3030
let acceptor_node = &nodes[acceptor_node_index];
31+
let initiator_node_id = initiator_node.node.get_our_node_id();
32+
let acceptor_node_id = acceptor_node.node.get_our_node_id();
3133

3234
let channel_value_sat = 100_000;
3335
let channel_reserve_amnt_sat = 1_000;
@@ -87,12 +89,16 @@ fn test_v1_splice_in() {
8789
None, // locktime
8890
)
8991
.unwrap();
92+
93+
let init_stfu = get_event_msg!(initiator_node, MessageSendEvent::SendStfu, acceptor_node_id);
94+
acceptor_node.node.handle_stfu(initiator_node_id, &init_stfu);
95+
96+
let ack_stfu = get_event_msg!(acceptor_node, MessageSendEvent::SendStfu, initiator_node_id);
97+
initiator_node.node.handle_stfu(acceptor_node_id, &ack_stfu);
98+
9099
// Extract the splice_init message
91-
let splice_init_msg = get_event_msg!(
92-
initiator_node,
93-
MessageSendEvent::SendSpliceInit,
94-
acceptor_node.node.get_our_node_id()
95-
);
100+
let splice_init_msg =
101+
get_event_msg!(initiator_node, MessageSendEvent::SendSpliceInit, acceptor_node_id);
96102
assert_eq!(splice_init_msg.funding_contribution_satoshis, splice_in_sats as i64);
97103
assert_eq!(splice_init_msg.funding_feerate_per_kw, funding_feerate_per_kw);
98104
assert_eq!(splice_init_msg.funding_pubkey.to_string(), expected_initiator_funding_key);

0 commit comments

Comments
 (0)