Use a SpliceContribution enum for passing splice-in params
What changed, and why it matters
This commit is a straightforward code refactor. It bundles several parameters related to adding funds to a Lightning channel (a 'splice-in') into a single new enum called SpliceContribution. The goal is to make the API cleaner and prepare it for a future 'splice-out' feature. There is no security fix or vulnerability here.
No security action needed. Treat as a normal API refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces a SpliceContribution enum in funding.rs to replace the individual i64 contribution, Vec
Changed components
lightning/src/ln/funding.rslightning/src/ln/channel.rslightning/src/ln/channelmanager.rslightning/src/ln/splicing_tests.rsInspect captured patch +75 / −27
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 2170f25..e23001e 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -58,6 +58,8 @@ use crate::ln::channelmanager::{
};
use crate::ln::funding::FundingTxInput;
#[cfg(splicing)]
+use crate::ln::funding::SpliceContribution;
+#[cfg(splicing)]
use crate::ln::interactivetxs::{
calculate_change_output_value, AbortReason, InteractiveTxMessageSend,
};
@@ -10603,8 +10605,7 @@ where
/// generated by `SignerProvider::get_destination_script`.
#[cfg(splicing)]
pub fn splice_channel(
- &mut self, our_funding_contribution_satoshis: i64, our_funding_inputs: Vec<FundingTxInput>,
- change_script: Option<ScriptBuf>, funding_feerate_per_kw: u32, locktime: u32,
+ &mut self, contribution: SpliceContribution, funding_feerate_per_kw: u32, locktime: u32,
) -> Result<msgs::SpliceInit, APIError> {
// Check if a splice has been initiated already.
// Note: only a single outstanding splice is supported (per spec)
@@ -10628,7 +10629,7 @@ where
// TODO(splicing): check for quiescence
- let our_funding_contribution = SignedAmount::from_sat(our_funding_contribution_satoshis);
+ let our_funding_contribution = contribution.value();
if our_funding_contribution > SignedAmount::MAX_MONEY {
return Err(APIError::APIMisuseError {
err: format!(
@@ -10657,7 +10658,7 @@ where
// Check that inputs are sufficient to cover our contribution.
let _fee = check_v2_funding_inputs_sufficient(
our_funding_contribution.to_sat(),
- &our_funding_inputs,
+ contribution.inputs(),
true,
true,
funding_feerate_per_kw,
@@ -10670,7 +10671,7 @@ where
),
})?;
- for FundingTxInput { utxo, prevtx, .. } in our_funding_inputs.iter() {
+ for FundingTxInput { utxo, prevtx, .. } in contribution.inputs().iter() {
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
channel_id: ChannelId([0; 32]),
serial_id: 0,
@@ -10692,6 +10693,7 @@ where
}
let prev_funding_input = self.funding.to_splice_funding_input();
+ let (our_funding_inputs, change_script) = contribution.into_tx_parts();
let funding_negotiation_context = FundingNegotiationContext {
is_initiator: true,
our_funding_contribution,
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index abb1049..3d14937 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -30,8 +30,6 @@ use bitcoin::hashes::{Hash, HashEngine, HmacEngine};
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, SecretKey};
-#[cfg(splicing)]
-use bitcoin::ScriptBuf;
use bitcoin::{secp256k1, Sequence, SignedAmount};
use crate::blinded_path::message::MessageForwardNode;
@@ -66,7 +64,7 @@ use crate::ln::channel::{
};
use crate::ln::channel_state::ChannelDetails;
#[cfg(splicing)]
-use crate::ln::funding::FundingTxInput;
+use crate::ln::funding::SpliceContribution;
use crate::ln::inbound_payment;
use crate::ln::interactivetxs::{HandleTxCompleteResult, InteractiveTxMessageSendResult};
use crate::ln::msgs;
@@ -4460,14 +4458,13 @@ where
#[cfg(splicing)]
#[rustfmt::skip]
pub fn splice_channel(
- &self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, our_funding_contribution_satoshis: i64,
- our_funding_inputs: Vec<FundingTxInput>, change_script: Option<ScriptBuf>,
- funding_feerate_per_kw: u32, locktime: Option<u32>,
+ &self, channel_id: &ChannelId, counterparty_node_id: &PublicKey,
+ contribution: SpliceContribution, funding_feerate_per_kw: u32, locktime: Option<u32>,
) -> Result<(), APIError> {
let mut res = Ok(());
PersistenceNotifierGuard::optionally_notify(self, || {
let result = self.internal_splice_channel(
- channel_id, counterparty_node_id, our_funding_contribution_satoshis, our_funding_inputs, change_script, funding_feerate_per_kw, locktime
+ channel_id, counterparty_node_id, contribution, funding_feerate_per_kw, locktime
);
res = result;
match res {
@@ -4482,8 +4479,7 @@ where
#[cfg(splicing)]
fn internal_splice_channel(
&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey,
- our_funding_contribution_satoshis: i64, our_funding_inputs: Vec<FundingTxInput>,
- change_script: Option<ScriptBuf>, funding_feerate_per_kw: u32, locktime: Option<u32>,
+ contribution: SpliceContribution, funding_feerate_per_kw: u32, locktime: Option<u32>,
) -> Result<(), APIError> {
let per_peer_state = self.per_peer_state.read().unwrap();
@@ -4504,13 +4500,8 @@ where
hash_map::Entry::Occupied(mut chan_phase_entry) => {
let locktime = locktime.unwrap_or_else(|| self.current_best_block().height);
if let Some(chan) = chan_phase_entry.get_mut().as_funded_mut() {
- let msg = chan.splice_channel(
- our_funding_contribution_satoshis,
- our_funding_inputs,
- change_script,
- funding_feerate_per_kw,
- locktime,
- )?;
+ let msg =
+ chan.splice_channel(contribution, funding_feerate_per_kw, locktime)?;
peer_state.pending_msg_events.push(MessageSendEvent::SendSpliceInit {
node_id: *counterparty_node_id,
msg,
diff --git a/lightning/src/ln/funding.rs b/lightning/src/ln/funding.rs
index 7dc5910..21bc42b 100644
--- a/lightning/src/ln/funding.rs
+++ b/lightning/src/ln/funding.rs
@@ -9,11 +9,54 @@
//! Types pertaining to funding channels.
+#[cfg(splicing)]
+use bitcoin::{Amount, ScriptBuf, SignedAmount};
use bitcoin::{Script, Sequence, Transaction, Weight};
use crate::events::bump_transaction::{Utxo, EMPTY_SCRIPT_SIG_WEIGHT};
use crate::sign::{P2TR_KEY_PATH_WITNESS_WEIGHT, P2WPKH_WITNESS_WEIGHT};
+/// The components of a splice's funding transaction that are contributed by one party.
+#[cfg(splicing)]
+pub enum SpliceContribution {
+ /// When funds are added to a channel.
+ SpliceIn {
+ /// The amount to contribute to the splice.
+ value: Amount,
+
+ /// The inputs included in the splice's funding transaction to meet the contributed amount.
+ /// Any excess amount will be sent to a change output.
+ inputs: Vec<FundingTxInput>,
+
+ /// An optional change output script. This will be used if needed or, when not set,
+ /// generated using [`SignerProvider::get_destination_script`].
+ change_script: Option<ScriptBuf>,
+ },
+}
+
+#[cfg(splicing)]
+impl SpliceContribution {
+ pub(super) fn value(&self) -> SignedAmount {
+ match self {
+ SpliceContribution::SpliceIn { value, .. } => {
+ value.to_signed().unwrap_or(SignedAmount::MAX)
+ },
+ }
+ }
+
+ pub(super) fn inputs(&self) -> &[FundingTxInput] {
+ match self {
+ SpliceContribution::SpliceIn { inputs, .. } => &inputs[..],
+ }
+ }
+
+ pub(super) fn into_tx_parts(self) -> (Vec<FundingTxInput>, Option<ScriptBuf>) {
+ match self {
+ SpliceContribution::SpliceIn { inputs, change_script, .. } => (inputs, change_script),
+ }
+ }
+}
+
/// An input to contribute to a channel's funding transaction either when using the v2 channel
/// establishment protocol or when splicing.
#[derive(Clone)]
diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs
index c1340c8..6061632 100644
--- a/lightning/src/ln/splicing_tests.rs
+++ b/lightning/src/ln/splicing_tests.rs
@@ -8,9 +8,12 @@
// licenses.
use crate::ln::functional_test_utils::*;
+use crate::ln::funding::SpliceContribution;
use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, MessageSendEvent};
use crate::util::errors::APIError;
+use bitcoin::Amount;
+
/// Splicing test, simple splice-in flow. Starts with opening a V1 channel first.
/// Builds on test_channel_open_simple()
#[test]
@@ -66,15 +69,20 @@ fn test_v1_splice_in() {
&initiator_node,
&[extra_splice_funding_input_sats],
);
+
+ let contribution = SpliceContribution::SpliceIn {
+ value: Amount::from_sat(splice_in_sats),
+ inputs: funding_inputs,
+ change_script: None,
+ };
+
// Initiate splice-in
let _res = initiator_node
.node
.splice_channel(
&channel_id,
&acceptor_node.node.get_our_node_id(),
- splice_in_sats as i64,
- funding_inputs,
- None, // change_script
+ contribution,
funding_feerate_per_kw,
None, // locktime
)
@@ -295,13 +303,17 @@ fn test_v1_splice_in_negative_insufficient_inputs() {
let funding_inputs =
create_dual_funding_utxos_with_prev_txs(&nodes[0], &[extra_splice_funding_input_sats]);
+ let contribution = SpliceContribution::SpliceIn {
+ value: Amount::from_sat(splice_in_sats),
+ inputs: funding_inputs,
+ change_script: None,
+ };
+
// Initiate splice-in, with insufficient input contribution
let res = nodes[0].node.splice_channel(
&channel_id,
&nodes[1].node.get_our_node_id(),
- splice_in_sats as i64,
- funding_inputs,
- None, // change_script
+ contribution,
1024, // funding_feerate_per_kw,
None, // locktime
);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.