Skip to content

Commit f5933e5

Browse files
committed
WIP: Add FundingNeeded event for splicing
Rather than requiring the user to pass FundingTxInputs when initiating a splice, generate a FundingNeeded event once the channel has become quiescent. This simplifies error handling and UTXO / change address clean-up by consolidating it in SpliceFailed event handling. Later, this event will be used for opportunistic contributions (i.e., when the counterparty wins quiescence or initiates), dual-funding, and RBF.
1 parent 3448da2 commit f5933e5

File tree

5 files changed

+338
-113
lines changed

5 files changed

+338
-113
lines changed

lightning/src/events/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::blinded_path::payment::{
2525
use crate::chain::transaction;
2626
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
2727
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
28+
use crate::ln::funding::FundingContribution;
2829
use crate::ln::types::ChannelId;
2930
use crate::ln::{msgs, LocalHTLCFailureReason};
3031
use crate::offers::invoice::Bolt12Invoice;
@@ -1816,6 +1817,28 @@ pub enum Event {
18161817
/// [`ChannelManager::respond_to_static_invoice_request`]: crate::ln::channelmanager::ChannelManager::respond_to_static_invoice_request
18171818
invoice_request: InvoiceRequest,
18181819
},
1820+
///
1821+
FundingNeeded {
1822+
/// The `channel_id` of the channel which you'll need to pass back into
1823+
/// [`ChannelManager::funding_contributed`].
1824+
///
1825+
/// [`ChannelManager::funding_contributed`]: crate::ln::channelmanager::ChannelManager::funding_contributed
1826+
channel_id: ChannelId,
1827+
/// The counterparty's `node_id`, which you'll need to pass back into
1828+
/// [`ChannelManager::funding_contributed`].
1829+
///
1830+
/// [`ChannelManager::funding_contributed`]: crate::ln::channelmanager::ChannelManager::funding_contributed
1831+
counterparty_node_id: PublicKey,
1832+
/// The `user_channel_id` value passed in for outbound channels, or for inbound channels if
1833+
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
1834+
/// `user_channel_id` will be randomized for inbound channels.
1835+
///
1836+
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
1837+
user_channel_id: u128,
1838+
1839+
///
1840+
contribution: FundingContribution,
1841+
},
18191842
/// Indicates that a channel funding transaction constructed interactively is ready to be
18201843
/// signed. This event will only be triggered if at least one input was contributed.
18211844
///
@@ -2347,6 +2370,10 @@ impl Writeable for Event {
23472370
(13, *contributed_outputs, optional_vec),
23482371
});
23492372
},
2373+
&Event::FundingNeeded { .. } => {
2374+
54u8.write(writer)?;
2375+
todo!();
2376+
},
23502377
// Note that, going forward, all new events must only write data inside of
23512378
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
23522379
// data via `write_tlv_fields`.
@@ -2978,6 +3005,7 @@ impl MaybeReadable for Event {
29783005
};
29793006
f()
29803007
},
3008+
54u8 => todo!(),
29813009
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
29823010
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
29833011
// reads.

lightning/src/ln/channel.rs

Lines changed: 118 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use crate::ln::channelmanager::{
5555
RAACommitmentOrder, SentHTLCId, BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT,
5656
MIN_CLTV_EXPIRY_DELTA,
5757
};
58-
use crate::ln::funding::{FundingTxInput, SpliceContribution};
58+
use crate::ln::funding::{FundingContribution, FundingTxInput, SpliceContribution};
5959
use crate::ln::interactivetxs::{
6060
calculate_change_output_value, get_output_weight, AbortReason, HandleTxCompleteValue,
6161
InteractiveTxConstructor, InteractiveTxConstructorArgs, InteractiveTxMessageSend,
@@ -2802,24 +2802,28 @@ impl_writeable_tlv_based!(SpliceInstructions, {
28022802

28032803
#[derive(Debug)]
28042804
pub(crate) enum QuiescentAction {
2805-
Splice(SpliceInstructions),
2805+
LegacySplice(SpliceInstructions),
2806+
Splice(SpliceContribution),
28062807
#[cfg(any(test, fuzzing))]
28072808
DoNothing,
28082809
}
28092810

28102811
pub(crate) enum StfuResponse {
28112812
Stfu(msgs::Stfu),
28122813
SpliceInit(msgs::SpliceInit),
2814+
FundingNeeded(FundingContribution),
28132815
}
28142816

28152817
#[cfg(any(test, fuzzing))]
28162818
impl_writeable_tlv_based_enum_upgradable!(QuiescentAction,
28172819
(0, DoNothing) => {},
2818-
{1, Splice} => (),
2820+
{1, LegacySplice} => (),
2821+
{2, Splice} => (),
28192822
);
28202823
#[cfg(not(any(test, fuzzing)))]
28212824
impl_writeable_tlv_based_enum_upgradable!(QuiescentAction,,
2822-
{1, Splice} => (),
2825+
{1, LegacySplice} => (),
2826+
{2, Splice} => (),
28232827
);
28242828

28252829
/// Wrapper around a [`Transaction`] useful for caching the result of [`Transaction::compute_txid`].
@@ -6495,7 +6499,7 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
64956499
}
64966500

64976501
fn check_splice_contribution_sufficient(
6498-
contribution: &SpliceContribution, is_initiator: bool, funding_feerate: FeeRate,
6502+
contribution: &FundingContribution, is_initiator: bool, funding_feerate: FeeRate,
64996503
) -> Result<SignedAmount, String> {
65006504
if contribution.inputs().is_empty() {
65016505
let estimated_fee = Amount::from_sat(estimate_v2_funding_transaction_fee(
@@ -6621,30 +6625,30 @@ fn check_v2_funding_inputs_sufficient(
66216625

66226626
/// Context for negotiating channels (dual-funded V2 open, splicing)
66236627
#[derive(Debug)]
6624-
pub(super) struct FundingNegotiationContext {
6628+
pub struct FundingNegotiationContext {
66256629
/// Whether we initiated the funding negotiation.
6626-
pub is_initiator: bool,
6630+
pub(super) is_initiator: bool,
66276631
/// The amount in satoshis we will be contributing to the channel.
6628-
pub our_funding_contribution: SignedAmount,
6632+
pub(super) our_funding_contribution: SignedAmount,
66296633
/// The funding transaction locktime suggested by the initiator. If set by us, it is always set
66306634
/// to the current block height to align incentives against fee-sniping.
6631-
pub funding_tx_locktime: LockTime,
6635+
pub(super) funding_tx_locktime: LockTime,
66326636
/// The feerate set by the initiator to be used for the funding transaction.
66336637
#[allow(dead_code)] // TODO(dual_funding): Remove once V2 channels is enabled.
6634-
pub funding_feerate_sat_per_1000_weight: u32,
6638+
pub(super) funding_feerate_sat_per_1000_weight: u32,
66356639
/// The input spending the previous funding output, if this is a splice.
66366640
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
6637-
pub shared_funding_input: Option<SharedOwnedInput>,
6641+
pub(super) shared_funding_input: Option<SharedOwnedInput>,
66386642
/// The funding inputs we will be contributing to the channel.
66396643
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6640-
pub our_funding_inputs: Vec<FundingTxInput>,
6644+
pub(super) our_funding_inputs: Vec<FundingTxInput>,
66416645
/// The funding outputs we will be contributing to the channel.
66426646
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
6643-
pub our_funding_outputs: Vec<TxOut>,
6647+
pub(super) our_funding_outputs: Vec<TxOut>,
66446648
/// The change output script. This will be used if needed or -- if not set -- generated using
66456649
/// `SignerProvider::get_destination_script`.
66466650
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
6647-
pub change_script: Option<ScriptBuf>,
6651+
pub(super) change_script: Option<ScriptBuf>,
66486652
}
66496653

66506654
impl FundingNegotiationContext {
@@ -6970,7 +6974,7 @@ where
69706974
self.reset_pending_splice_state()
69716975
} else {
69726976
match self.quiescent_action.take() {
6973-
Some(QuiescentAction::Splice(instructions)) => {
6977+
Some(QuiescentAction::LegacySplice(instructions)) => {
69746978
self.context.channel_state.clear_awaiting_quiescence();
69756979
let (inputs, outputs) = instructions.into_contributed_inputs_and_outputs();
69766980
Some(SpliceFundingFailed {
@@ -6980,6 +6984,15 @@ where
69806984
contributed_outputs: outputs,
69816985
})
69826986
},
6987+
Some(QuiescentAction::Splice(contribution)) => {
6988+
self.context.channel_state.clear_awaiting_quiescence();
6989+
Some(SpliceFundingFailed {
6990+
funding_txo: None,
6991+
channel_type: None,
6992+
contributed_inputs: vec![],
6993+
contributed_outputs: contribution.into_outputs(),
6994+
})
6995+
},
69836996
#[cfg(any(test, fuzzing))]
69846997
Some(quiescent_action) => {
69856998
self.quiescent_action = Some(quiescent_action);
@@ -11274,7 +11287,7 @@ where
1127411287
self.get_announcement_sigs(node_signer, chain_hash, user_config, block_height, logger);
1127511288

1127611289
if let Some(quiescent_action) = self.quiescent_action.as_ref() {
11277-
if matches!(quiescent_action, QuiescentAction::Splice(_)) {
11290+
if matches!(quiescent_action, QuiescentAction::Splice(_) | QuiescentAction::LegacySplice(_)) {
1127811291
self.context.channel_state.set_awaiting_quiescence();
1127911292
}
1128011293
}
@@ -11924,8 +11937,7 @@ where
1192411937
/// - `change_script`: an option change output script. If `None` and needed, one will be
1192511938
/// generated by `SignerProvider::get_destination_script`.
1192611939
pub fn splice_channel<L: Deref>(
11927-
&mut self, contribution: SpliceContribution, funding_feerate_per_kw: u32, locktime: u32,
11928-
logger: &L,
11940+
&mut self, contribution: SpliceContribution, logger: &L,
1192911941
) -> Result<Option<msgs::Stfu>, APIError>
1193011942
where
1193111943
L::Target: Logger,
@@ -11939,8 +11951,15 @@ where
1193911951
});
1194011952
}
1194111953

11942-
// Check if a splice has been initiated already.
11943-
// Note: only a single outstanding splice is supported (per spec)
11954+
if self.context.channel_state.is_quiescent() {
11955+
return Err(APIError::APIMisuseError {
11956+
err: format!(
11957+
"Channel {} cannot be spliced as it is already quiescent",
11958+
self.context.channel_id(),
11959+
),
11960+
});
11961+
}
11962+
1194411963
if self.pending_splice.is_some() || self.quiescent_action.is_some() {
1194511964
return Err(APIError::APIMisuseError {
1194611965
err: format!(
@@ -11969,71 +11988,71 @@ where
1196911988
});
1197011989
}
1197111990

11972-
// Fees for splice-out are paid from the channel balance whereas fees for splice-in
11973-
// are paid by the funding inputs. Therefore, in the case of splice-out, we add the
11974-
// fees on top of the user-specified contribution. We leave the user-specified
11975-
// contribution as-is for splice-ins.
11976-
let adjusted_funding_contribution = check_splice_contribution_sufficient(
11977-
&contribution,
11978-
true,
11979-
FeeRate::from_sat_per_kwu(u64::from(funding_feerate_per_kw)),
11980-
)
11981-
.map_err(|e| APIError::APIMisuseError {
11982-
err: format!(
11983-
"Channel {} cannot be {}; {}",
11984-
self.context.channel_id(),
11985-
if our_funding_contribution.is_positive() { "spliced in" } else { "spliced out" },
11986-
e
11987-
),
11988-
})?;
11991+
//// Fees for splice-out are paid from the channel balance whereas fees for splice-in
11992+
//// are paid by the funding inputs. Therefore, in the case of splice-out, we add the
11993+
//// fees on top of the user-specified contribution. We leave the user-specified
11994+
//// contribution as-is for splice-ins.
11995+
//let adjusted_funding_contribution = check_splice_contribution_sufficient(
11996+
// &contribution,
11997+
// true,
11998+
// FeeRate::from_sat_per_kwu(u64::from(funding_feerate_per_kw)),
11999+
//)
12000+
//.map_err(|e| APIError::APIMisuseError {
12001+
// err: format!(
12002+
// "Channel {} cannot be {}; {}",
12003+
// self.context.channel_id(),
12004+
// if our_funding_contribution.is_positive() { "spliced in" } else { "spliced out" },
12005+
// e
12006+
// ),
12007+
//})?;
1198912008

1199012009
// Note: post-splice channel value is not yet known at this point, counterparty contribution is not known
1199112010
// (Cannot test for miminum required post-splice channel value)
1199212011
let their_funding_contribution = SignedAmount::ZERO;
1199312012
self.validate_splice_contributions(
11994-
adjusted_funding_contribution,
12013+
//adjusted_funding_contribution,
12014+
our_funding_contribution,
1199512015
their_funding_contribution,
1199612016
)
1199712017
.map_err(|err| APIError::APIMisuseError { err })?;
1199812018

11999-
for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
12000-
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
12001-
channel_id: ChannelId([0; 32]),
12002-
serial_id: 0,
12003-
prevtx: None,
12004-
prevtx_out: 0,
12005-
sequence: 0,
12006-
// Mutually exclusive with prevtx, which is accounted for below.
12007-
shared_input_txid: None,
12008-
};
12009-
let message_len = MESSAGE_TEMPLATE.serialized_length() + prevtx.serialized_length();
12010-
if message_len > LN_MAX_MSG_LEN {
12011-
return Err(APIError::APIMisuseError {
12012-
err: format!(
12013-
"Funding input references a prevtx that is too large for tx_add_input: {}",
12014-
utxo.outpoint,
12015-
),
12016-
});
12017-
}
12018-
}
12019-
12020-
let (our_funding_inputs, our_funding_outputs, change_script) = contribution.into_tx_parts();
12021-
12022-
let action = QuiescentAction::Splice(SpliceInstructions {
12023-
adjusted_funding_contribution,
12024-
our_funding_inputs,
12025-
our_funding_outputs,
12026-
change_script,
12027-
funding_feerate_per_kw,
12028-
locktime,
12029-
});
12030-
self.propose_quiescence(logger, action)
12019+
//for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
12020+
// const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
12021+
// channel_id: ChannelId([0; 32]),
12022+
// serial_id: 0,
12023+
// prevtx: None,
12024+
// prevtx_out: 0,
12025+
// sequence: 0,
12026+
// // Mutually exclusive with prevtx, which is accounted for below.
12027+
// shared_input_txid: None,
12028+
// };
12029+
// let message_len = MESSAGE_TEMPLATE.serialized_length() + prevtx.serialized_length();
12030+
// if message_len > LN_MAX_MSG_LEN {
12031+
// return Err(APIError::APIMisuseError {
12032+
// err: format!(
12033+
// "Funding input references a prevtx that is too large for tx_add_input: {}",
12034+
// utxo.outpoint,
12035+
// ),
12036+
// });
12037+
// }
12038+
//}
12039+
12040+
self.propose_quiescence(logger, QuiescentAction::Splice(contribution))
1203112041
.map_err(|e| APIError::APIMisuseError { err: e.to_owned() })
1203212042
}
1203312043

12034-
fn send_splice_init(&mut self, instructions: SpliceInstructions) -> msgs::SpliceInit {
12035-
debug_assert!(self.pending_splice.is_none());
12044+
pub fn funding_contributed<L: Deref>(
12045+
&mut self, context: FundingNegotiationContext, logger: &L,
12046+
) -> Result<msgs::SpliceInit, APIError>
12047+
where
12048+
L::Target: Logger,
12049+
{
12050+
// TODO: Add any checks or move them to FundingNegotiationContext construction. Probably
12051+
// emit a `SpliceFailed` event instead of returning an error
12052+
Ok(self.send_splice_init_internal(context))
12053+
}
1203612054

12055+
fn send_splice_init(&mut self, instructions: SpliceInstructions) -> msgs::SpliceInit {
1203712056
let SpliceInstructions {
1203812057
adjusted_funding_contribution,
1203912058
our_funding_inputs,
@@ -12055,6 +12074,11 @@ where
1205512074
change_script,
1205612075
};
1205712076

12077+
self.send_splice_init_internal(context)
12078+
}
12079+
12080+
fn send_splice_init_internal(&mut self, context: FundingNegotiationContext) -> msgs::SpliceInit {
12081+
debug_assert!(self.pending_splice.is_none());
1205812082
// Rotate the funding pubkey using the prev_funding_txid as a tweak
1205912083
let prev_funding_txid = self.funding.get_funding_txid();
1206012084
let funding_pubkey = match (prev_funding_txid, &self.context.holder_signer) {
@@ -12069,6 +12093,10 @@ where
1206912093
_ => todo!(),
1207012094
};
1207112095

12096+
let funding_feerate_per_kw = context.funding_feerate_sat_per_1000_weight;
12097+
let funding_contribution_satoshis = context.our_funding_contribution.to_sat();
12098+
let locktime = context.funding_tx_locktime.to_consensus_u32();
12099+
1207212100
let funding_negotiation =
1207312101
FundingNegotiation::AwaitingAck { context, new_holder_funding_key: funding_pubkey };
1207412102
self.pending_splice = Some(PendingFunding {
@@ -12080,7 +12108,7 @@ where
1208012108

1208112109
msgs::SpliceInit {
1208212110
channel_id: self.context.channel_id,
12083-
funding_contribution_satoshis: adjusted_funding_contribution.to_sat(),
12111+
funding_contribution_satoshis,
1208412112
funding_feerate_per_kw,
1208512113
locktime,
1208612114
funding_pubkey,
@@ -13302,9 +13330,9 @@ where
1330213330
"Internal Error: Didn't have anything to do after reaching quiescence".to_owned()
1330313331
));
1330413332
},
13305-
Some(QuiescentAction::Splice(instructions)) => {
13333+
Some(QuiescentAction::LegacySplice(instructions)) => {
1330613334
if self.pending_splice.is_some() {
13307-
self.quiescent_action = Some(QuiescentAction::Splice(instructions));
13335+
self.quiescent_action = Some(QuiescentAction::LegacySplice(instructions));
1330813336

1330913337
return Err(ChannelError::WarnAndDisconnect(
1331013338
format!(
@@ -13317,6 +13345,21 @@ where
1331713345
let splice_init = self.send_splice_init(instructions);
1331813346
return Ok(Some(StfuResponse::SpliceInit(splice_init)));
1331913347
},
13348+
Some(QuiescentAction::Splice(contribution)) => {
13349+
if self.pending_splice.is_some() {
13350+
self.quiescent_action = Some(QuiescentAction::Splice(contribution));
13351+
13352+
return Err(ChannelError::WarnAndDisconnect(
13353+
format!(
13354+
"Channel {} cannot be spliced as it already has a splice pending",
13355+
self.context.channel_id(),
13356+
),
13357+
));
13358+
}
13359+
13360+
let contribution = contribution.into_funding_contribution();
13361+
return Ok(Some(StfuResponse::FundingNeeded(contribution)));
13362+
},
1332013363
#[cfg(any(test, fuzzing))]
1332113364
Some(QuiescentAction::DoNothing) => {
1332213365
// In quiescence test we want to just hang out here, letting the test manually

0 commit comments

Comments
 (0)