Skip to content

Commit c675ca1

Browse files
committed
New RefundingScope; FundedChannel can act as pending
FundedChannel is extended with an optional struct RefundingScope, that holds data used during splicing (re)negotiation. It stores the same fields as PendingV2Channel, excet for the context. FundedChannel can act as a transaction constructor (much like PendingV2Channel), when the refunding context is present.
1 parent 3e999f8 commit c675ca1

File tree

1 file changed

+122
-9
lines changed

1 file changed

+122
-9
lines changed

lightning/src/ln/channel.rs

Lines changed: 122 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,14 @@ impl<SP: Deref> Channel<SP> where
12531253
pub fn unfunded_context_mut(&mut self) -> Option<&mut UnfundedChannelContext> {
12541254
match &mut self.phase {
12551255
ChannelPhase::Undefined => unreachable!(),
1256-
ChannelPhase::Funded(_) => { debug_assert!(false); None },
1256+
#[cfg(not(splicing))]
1257+
ChannelPhase::Funded(_) => { debug_assert!(false); None }
1258+
#[cfg(splicing)]
1259+
ChannelPhase::Funded(chan) => {
1260+
chan.pending_splice.as_mut().map(|splice|
1261+
splice.refunding_scope.as_mut().map(|refunding_scope| &mut refunding_scope.pending_unfunded_context)
1262+
).flatten()
1263+
}
12571264
ChannelPhase::UnfundedOutboundV1(chan) => Some(&mut chan.unfunded_context),
12581265
ChannelPhase::UnfundedInboundV1(chan) => Some(&mut chan.unfunded_context),
12591266
ChannelPhase::UnfundedV2(chan) => Some(&mut chan.unfunded_context),
@@ -1801,12 +1808,19 @@ impl FundingScope {
18011808
}
18021809
}
18031810

1804-
/// Info about a pending splice, used in the pre-splice channel
1811+
/// Info about a pending splice_init, used in the pre-splice channel
18051812
#[cfg(splicing)]
1806-
struct PendingSplice {
1813+
struct PendingSpliceInit {
18071814
pub our_funding_contribution: i64,
18081815
}
18091816

1817+
/// Info about a pending splice
1818+
#[cfg(splicing)]
1819+
struct PendingSplice {
1820+
pub pending_splice_init: Option<PendingSpliceInit>,
1821+
pub refunding_scope: Option<RefundingScope>,
1822+
}
1823+
18101824
/// Contains everything about the channel including state, and various flags.
18111825
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
18121826
config: LegacyChannelConfig,
@@ -2302,6 +2316,81 @@ impl<SP: Deref> InitialRemoteCommitmentReceiver<SP> for FundedChannel<SP> where
23022316
}
23032317
}
23042318

2319+
/// [`FundedChannel`] can act as a [`FundingTxConstructorV2`], but properly only when its
2320+
/// [`RefundingScope`] is present.
2321+
#[cfg(splicing)]
2322+
impl<SP: Deref> FundingTxConstructorV2<SP> for FundedChannel<SP> where SP::Target: SignerProvider {
2323+
#[inline]
2324+
fn pending_funding(&self) -> Result<&FundingScope, &'static str> {
2325+
self.pending_splice.as_ref().map(|splice|
2326+
splice.refunding_scope.as_ref().map(|refunding| &refunding.pending_funding)
2327+
).flatten().ok_or("Not re-funding")
2328+
}
2329+
2330+
#[inline]
2331+
fn pending_funding_mut(&mut self) -> Result<&mut FundingScope, &'static str> {
2332+
self.pending_splice.as_mut().map(|splice|
2333+
splice.refunding_scope.as_mut().map(|refunding| &mut refunding.pending_funding)
2334+
).flatten().ok_or("Not re-funding")
2335+
}
2336+
2337+
#[inline]
2338+
fn pending_funding_and_context_mut(&mut self) -> Result<(&FundingScope, &mut ChannelContext<SP>), &'static str> {
2339+
let context_mut = &mut self.context;
2340+
self.pending_splice.as_ref().map(|splice|
2341+
splice.refunding_scope.as_ref().map(|refunding| (&refunding.pending_funding, context_mut))
2342+
).flatten().ok_or("Not re-funding")
2343+
}
2344+
2345+
#[inline]
2346+
fn dual_funding_context(&self) -> Result<&DualFundingChannelContext, &'static str> {
2347+
self.pending_splice.as_ref().map(|splice|
2348+
splice.refunding_scope.as_ref().map(|refunding| &refunding.pending_dual_funding_context)
2349+
).flatten().ok_or("Not re-funding")
2350+
}
2351+
2352+
fn swap_out_dual_funding_context_inputs(&mut self, funding_inputs: &mut Vec<(TxIn, TransactionU16LenLimited)>) -> Result<(), &'static str> {
2353+
if let Some(pending_splice) = &mut self.pending_splice {
2354+
if let Some(refunding) = &mut pending_splice.refunding_scope {
2355+
mem::swap(&mut refunding.pending_dual_funding_context.our_funding_inputs, funding_inputs);
2356+
Ok(())
2357+
} else {
2358+
Err("Not re-funding")
2359+
}
2360+
} else {
2361+
Err("Not re-funding")
2362+
}
2363+
}
2364+
2365+
#[inline]
2366+
fn unfunded_context(&self) -> Result<&UnfundedChannelContext, &'static str> {
2367+
self.pending_splice.as_ref().map(|splice|
2368+
splice.refunding_scope.as_ref().map(|refunding| &refunding.pending_unfunded_context)
2369+
).flatten().ok_or("Not re-funding")
2370+
}
2371+
2372+
#[inline]
2373+
fn interactive_tx_constructor(&self) -> Result<Option<&InteractiveTxConstructor>, &'static str> {
2374+
self.pending_splice.as_ref().map(|splice|
2375+
splice.refunding_scope.as_ref().map(|refunding| refunding.pending_interactive_tx_constructor.as_ref())
2376+
).flatten().ok_or("Not re-funding")
2377+
}
2378+
2379+
#[inline]
2380+
fn interactive_tx_constructor_mut(&mut self) -> Result<&mut Option<InteractiveTxConstructor>, &'static str> {
2381+
self.pending_splice.as_mut().map(|splice|
2382+
splice.refunding_scope.as_mut().map(|refunding| &mut refunding.pending_interactive_tx_constructor)
2383+
).flatten().ok_or("Not re-funding")
2384+
}
2385+
2386+
#[inline]
2387+
fn interactive_tx_signing_session_mut(&mut self) -> Result<&mut Option<InteractiveTxSigningSession>, &'static str> {
2388+
self.pending_splice.as_mut().map(|splice|
2389+
splice.refunding_scope.as_mut().map(|refunding| &mut refunding.pending_interactive_tx_signing_session)
2390+
).flatten().ok_or("Not re-funding")
2391+
}
2392+
}
2393+
23052394
/// A channel struct implementing this trait can perform V2 transaction negotiation,
23062395
/// either at channel open or during splicing.
23072396
/// Accessors return a Result, because [`FundedChannel`] can act like a TX constructor,
@@ -2659,6 +2748,19 @@ impl<SP: Deref> FundingTxConstructorV2<SP> for PendingV2Channel<SP> where SP::Ta
26592748
}
26602749
}
26612750

2751+
/// Data needed during splicing --
2752+
/// when the funding transaction is being renegotiated in a funded channel.
2753+
#[cfg(splicing)]
2754+
struct RefundingScope {
2755+
// Fields belonging for [`PendingV2Channel`], except the context
2756+
pending_funding: FundingScope,
2757+
pending_unfunded_context: UnfundedChannelContext,
2758+
pending_dual_funding_context: DualFundingChannelContext,
2759+
/// The current interactive transaction construction session under negotiation.
2760+
pending_interactive_tx_constructor: Option<InteractiveTxConstructor>,
2761+
pending_interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
2762+
}
2763+
26622764
impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
26632765
fn new_for_inbound_channel<'a, ES: Deref, F: Deref, L: Deref>(
26642766
fee_estimator: &'a LowerBoundedFeeEstimator<F>,
@@ -8726,10 +8828,11 @@ impl<SP: Deref> FundedChannel<SP> where
87268828
) -> Result<msgs::SpliceInit, APIError> {
87278829
// Check if a splice has been initiated already.
87288830
// Note: only a single outstanding splice is supported (per spec)
8729-
if let Some(splice_info) = &self.pending_splice {
8831+
if let Some(pending_splice) = &self.pending_splice {
87308832
return Err(APIError::APIMisuseError { err: format!(
87318833
"Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
8732-
self.context.channel_id(), splice_info.our_funding_contribution
8834+
self.context.channel_id(),
8835+
pending_splice.pending_splice_init.as_ref().map(|ps| ps.our_funding_contribution).unwrap_or_default(),
87338836
)});
87348837
}
87358838

@@ -8762,9 +8865,13 @@ impl<SP: Deref> FundedChannel<SP> where
87628865
self.context.channel_id(), err,
87638866
)})?;
87648867

8765-
self.pending_splice = Some(PendingSplice {
8868+
let pending_splice_init = Some(PendingSpliceInit {
87668869
our_funding_contribution: our_funding_contribution_satoshis,
87678870
});
8871+
self.pending_splice = Some(PendingSplice {
8872+
pending_splice_init,
8873+
refunding_scope: None,
8874+
});
87688875

87698876
let msg = self.get_splice_init(our_funding_contribution_satoshis, funding_feerate_per_kw, locktime);
87708877
Ok(msg)
@@ -8796,9 +8903,11 @@ impl<SP: Deref> FundedChannel<SP> where
87968903
let our_funding_contribution_satoshis = 0i64;
87978904

87988905
// Check if a splice has been initiated already.
8799-
if let Some(splice_info) = &self.pending_splice {
8906+
if let Some(pending_splice) = &self.pending_splice {
88008907
return Err(ChannelError::Warn(format!(
8801-
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8908+
"Channel {} has already a splice pending, contribution {}",
8909+
self.context.channel_id(),
8910+
pending_splice.pending_splice_init.as_ref().map(|si| si.our_funding_contribution).unwrap_or_default(),
88028911
)));
88038912
}
88048913

@@ -8843,7 +8952,11 @@ impl<SP: Deref> FundedChannel<SP> where
88438952
#[cfg(splicing)]
88448953
pub fn splice_ack(&mut self, _msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
88458954
// check if splice is pending
8846-
if self.pending_splice.is_none() {
8955+
if let Some(pending_splice) = &self.pending_splice {
8956+
if pending_splice.pending_splice_init.is_none() {
8957+
return Err(ChannelError::Warn(format!("Channel is not in pending splice_init")));
8958+
}
8959+
} else {
88478960
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
88488961
};
88498962

0 commit comments

Comments
 (0)