Skip to content

Commit 734ad69

Browse files
committed
Support funding_transaction_signed for unfunded dual-funded channels
Now that we require users to first call `ChannelManager::funding_transaction_signed` before releasing any signatures, it's possible that it is called before we receive the initial commitment signed from our counterparty, which would transition the channel to funded. Because of this, we need to support the API call while the channel is still in the unfunded phase. Note that this commit is mostly a code move of `FundedChannel::funding_transaction_signed` to `Channel::funding_transaction_signed` that doesn't alter the signing logic.
1 parent e39fb8a commit 734ad69

File tree

2 files changed

+305
-275
lines changed

2 files changed

+305
-275
lines changed

lightning/src/ln/channel.rs

Lines changed: 189 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,11 +1954,19 @@ where
19541954

19551955
let funding_tx_signed = if !has_local_contribution {
19561956
let funding_txid = signing_session.unsigned_tx().tx().compute_txid();
1957-
if let ChannelPhase::Funded(chan) = &mut self.phase {
1958-
chan.funding_transaction_signed(funding_txid, vec![], 0, fee_estimator, logger).ok()
1959-
} else {
1960-
None
1961-
}
1957+
Some(
1958+
self.funding_transaction_signed(funding_txid, vec![], 0, fee_estimator, logger)
1959+
.map_err(|err| {
1960+
log_error!(
1961+
logger,
1962+
"Failed signing funding transaction without local contribution: {err:?}"
1963+
);
1964+
self.fail_interactive_tx_negotiation(
1965+
AbortReason::InternalError("Signing failed"),
1966+
logger,
1967+
)
1968+
})?,
1969+
)
19621970
} else {
19631971
None
19641972
};
@@ -2144,6 +2152,180 @@ where
21442152
Ok(())
21452153
}
21462154

2155+
pub fn funding_transaction_signed<F: Deref, L: Deref>(
2156+
&mut self, funding_txid_signed: Txid, witnesses: Vec<Witness>, best_block_height: u32,
2157+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
2158+
) -> Result<FundingTxSigned, APIError>
2159+
where
2160+
F::Target: FeeEstimator,
2161+
L::Target: Logger,
2162+
{
2163+
let (context, funding, pending_splice) = match &mut self.phase {
2164+
ChannelPhase::Undefined => unreachable!(),
2165+
ChannelPhase::UnfundedV2(channel) => (&mut channel.context, &channel.funding, None),
2166+
ChannelPhase::Funded(channel) => {
2167+
(&mut channel.context, &channel.funding, channel.pending_splice.as_ref())
2168+
},
2169+
_ => {
2170+
return Err(APIError::APIMisuseError {
2171+
err: format!(
2172+
"Channel with id {} not expecting funding signatures",
2173+
self.context().channel_id
2174+
),
2175+
});
2176+
},
2177+
};
2178+
2179+
let signing_session = if let Some(signing_session) =
2180+
context.interactive_tx_signing_session.as_mut()
2181+
{
2182+
if let Some(pending_splice) = pending_splice.as_ref() {
2183+
debug_assert!(pending_splice
2184+
.funding_negotiation
2185+
.as_ref()
2186+
.map(|funding_negotiation| matches!(
2187+
funding_negotiation,
2188+
FundingNegotiation::AwaitingSignatures { .. }
2189+
))
2190+
.unwrap_or(false));
2191+
}
2192+
2193+
if signing_session.holder_tx_signatures().is_some() {
2194+
// Our `tx_signatures` either should've been the first time we processed them,
2195+
// or we're waiting for our counterparty to send theirs first.
2196+
return Ok(FundingTxSigned {
2197+
commitment_signed: None,
2198+
counterparty_initial_commitment_signed_result: None,
2199+
tx_signatures: None,
2200+
funding_tx: None,
2201+
splice_negotiated: None,
2202+
splice_locked: None,
2203+
});
2204+
}
2205+
2206+
signing_session
2207+
} else {
2208+
if Some(funding_txid_signed) == funding.get_funding_txid() {
2209+
// We may be handling a duplicate call and the funding was already locked so we
2210+
// no longer have the signing session present.
2211+
return Ok(FundingTxSigned {
2212+
commitment_signed: None,
2213+
counterparty_initial_commitment_signed_result: None,
2214+
tx_signatures: None,
2215+
funding_tx: None,
2216+
splice_negotiated: None,
2217+
splice_locked: None,
2218+
});
2219+
}
2220+
let err = format!("Channel {} not expecting funding signatures", context.channel_id);
2221+
return Err(APIError::APIMisuseError { err });
2222+
};
2223+
2224+
let tx = signing_session.unsigned_tx().tx();
2225+
if funding_txid_signed != tx.compute_txid() {
2226+
return Err(APIError::APIMisuseError {
2227+
err: "Transaction was malleated prior to signing".to_owned(),
2228+
});
2229+
}
2230+
2231+
let shared_input_signature =
2232+
if let Some(splice_input_index) = signing_session.unsigned_tx().shared_input_index() {
2233+
let sig = match &context.holder_signer {
2234+
ChannelSignerType::Ecdsa(signer) => signer.sign_splice_shared_input(
2235+
&funding.channel_transaction_parameters,
2236+
tx,
2237+
splice_input_index as usize,
2238+
&context.secp_ctx,
2239+
),
2240+
#[cfg(taproot)]
2241+
ChannelSignerType::Taproot(_) => todo!(),
2242+
};
2243+
Some(sig)
2244+
} else {
2245+
None
2246+
};
2247+
debug_assert_eq!(pending_splice.is_some(), shared_input_signature.is_some());
2248+
2249+
let tx_signatures = msgs::TxSignatures {
2250+
channel_id: context.channel_id,
2251+
tx_hash: funding_txid_signed,
2252+
witnesses,
2253+
shared_input_signature,
2254+
};
2255+
let (tx_signatures, funding_tx) = signing_session
2256+
.provide_holder_witnesses(tx_signatures, &context.secp_ctx)
2257+
.map_err(|err| APIError::APIMisuseError { err })?;
2258+
2259+
let logger = WithChannelContext::from(logger, &context, None);
2260+
if tx_signatures.is_some() {
2261+
log_info!(
2262+
logger,
2263+
"Sending tx_signatures for interactive funding transaction {funding_txid_signed}"
2264+
);
2265+
}
2266+
2267+
let funding = pending_splice
2268+
.as_ref()
2269+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
2270+
.and_then(|funding_negotiation| funding_negotiation.as_funding())
2271+
.unwrap_or(funding);
2272+
let commitment_signed = context.get_initial_commitment_signed_v2(funding, &&logger);
2273+
2274+
// In the common case for zero conf channels, we don't expect the funding transaction to be
2275+
// ready for broadcast yet as our counterparty shouldn't have sent their `tx_signatures`
2276+
// without us having sent our initial commitment signed to them first. However, in the event
2277+
// they do, we choose to handle it anyway.
2278+
let (splice_negotiated, splice_locked) = if let Some(funding_tx) = funding_tx.clone() {
2279+
debug_assert!(tx_signatures.is_some());
2280+
let funded_channel = self.as_funded_mut().expect(
2281+
"Funding transactions ready for broadcast can only exist for funded channels",
2282+
);
2283+
funded_channel.on_tx_signatures_exchange(funding_tx, best_block_height, &logger)
2284+
} else {
2285+
(None, None)
2286+
};
2287+
2288+
// If we have a pending splice with a buffered initial commitment signed from our
2289+
// counterparty, process it now that we have provided our signatures.
2290+
let counterparty_initial_commitment_signed_result =
2291+
if let Some(funded_channel) = self.as_funded_mut() {
2292+
if let Some(commit_sig) = funded_channel
2293+
.pending_splice
2294+
.as_mut()
2295+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_mut())
2296+
.and_then(|funding_negotiation| {
2297+
if let FundingNegotiation::AwaitingSignatures {
2298+
ref mut initial_commitment_signed_from_counterparty,
2299+
..
2300+
} = funding_negotiation
2301+
{
2302+
initial_commitment_signed_from_counterparty.take()
2303+
} else {
2304+
None
2305+
}
2306+
}) {
2307+
Some(funded_channel.splice_initial_commitment_signed(
2308+
&commit_sig,
2309+
fee_estimator,
2310+
&&logger,
2311+
))
2312+
} else {
2313+
None
2314+
}
2315+
} else {
2316+
None
2317+
};
2318+
2319+
Ok(FundingTxSigned {
2320+
commitment_signed,
2321+
counterparty_initial_commitment_signed_result,
2322+
tx_signatures,
2323+
funding_tx,
2324+
splice_negotiated,
2325+
splice_locked,
2326+
})
2327+
}
2328+
21472329
pub fn force_shutdown(&mut self, closure_reason: ClosureReason) -> ShutdownResult {
21482330
let (funding, context) = self.funding_and_context_mut();
21492331
context.force_shutdown(funding, closure_reason)
@@ -2213,7 +2395,7 @@ where
22132395
.unwrap_or(false);
22142396

22152397
// We delay processing this until the user manually approves the splice via
2216-
// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2398+
// [`Channel::funding_transaction_signed`], as otherwise, there would be a
22172399
// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would
22182400
// need to undo if they no longer wish to proceed.
22192401
if has_holder_tx_signatures {
@@ -2717,7 +2899,7 @@ enum FundingNegotiation {
27172899
is_initiator: bool,
27182900
/// The initial [`msgs::CommitmentSigned`] message received for the [`FundingScope`] above.
27192901
/// We delay processing this until the user manually approves the splice via
2720-
/// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2902+
/// [`Channel::funding_transaction_signed`], as otherwise, there would be a
27212903
/// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would need to undo
27222904
/// if they no longer wish to proceed.
27232905
///
@@ -9124,149 +9306,6 @@ where
91249306
}
91259307
}
91269308

9127-
pub fn funding_transaction_signed<F: Deref, L: Deref>(
9128-
&mut self, funding_txid_signed: Txid, witnesses: Vec<Witness>, best_block_height: u32,
9129-
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
9130-
) -> Result<FundingTxSigned, APIError>
9131-
where
9132-
F::Target: FeeEstimator,
9133-
L::Target: Logger,
9134-
{
9135-
let signing_session =
9136-
if let Some(signing_session) = self.context.interactive_tx_signing_session.as_mut() {
9137-
if let Some(pending_splice) = self.pending_splice.as_ref() {
9138-
debug_assert!(pending_splice
9139-
.funding_negotiation
9140-
.as_ref()
9141-
.map(|funding_negotiation| matches!(
9142-
funding_negotiation,
9143-
FundingNegotiation::AwaitingSignatures { .. }
9144-
))
9145-
.unwrap_or(false));
9146-
}
9147-
9148-
if signing_session.holder_tx_signatures().is_some() {
9149-
// Our `tx_signatures` either should've been the first time we processed them,
9150-
// or we're waiting for our counterparty to send theirs first.
9151-
return Ok(FundingTxSigned {
9152-
commitment_signed: None,
9153-
counterparty_initial_commitment_signed_result: None,
9154-
tx_signatures: None,
9155-
funding_tx: None,
9156-
splice_negotiated: None,
9157-
splice_locked: None,
9158-
});
9159-
}
9160-
9161-
signing_session
9162-
} else {
9163-
if Some(funding_txid_signed) == self.funding.get_funding_txid() {
9164-
// We may be handling a duplicate call and the funding was already locked so we
9165-
// no longer have the signing session present.
9166-
return Ok(FundingTxSigned {
9167-
commitment_signed: None,
9168-
counterparty_initial_commitment_signed_result: None,
9169-
tx_signatures: None,
9170-
funding_tx: None,
9171-
splice_negotiated: None,
9172-
splice_locked: None,
9173-
});
9174-
}
9175-
let err =
9176-
format!("Channel {} not expecting funding signatures", self.context.channel_id);
9177-
return Err(APIError::APIMisuseError { err });
9178-
};
9179-
9180-
let tx = signing_session.unsigned_tx().tx();
9181-
if funding_txid_signed != tx.compute_txid() {
9182-
return Err(APIError::APIMisuseError {
9183-
err: "Transaction was malleated prior to signing".to_owned(),
9184-
});
9185-
}
9186-
9187-
let shared_input_signature =
9188-
if let Some(splice_input_index) = signing_session.unsigned_tx().shared_input_index() {
9189-
let sig = match &self.context.holder_signer {
9190-
ChannelSignerType::Ecdsa(signer) => signer.sign_splice_shared_input(
9191-
&self.funding.channel_transaction_parameters,
9192-
tx,
9193-
splice_input_index as usize,
9194-
&self.context.secp_ctx,
9195-
),
9196-
#[cfg(taproot)]
9197-
ChannelSignerType::Taproot(_) => todo!(),
9198-
};
9199-
Some(sig)
9200-
} else {
9201-
None
9202-
};
9203-
debug_assert_eq!(self.pending_splice.is_some(), shared_input_signature.is_some());
9204-
9205-
let tx_signatures = msgs::TxSignatures {
9206-
channel_id: self.context.channel_id,
9207-
tx_hash: funding_txid_signed,
9208-
witnesses,
9209-
shared_input_signature,
9210-
};
9211-
let (tx_signatures, funding_tx) = signing_session
9212-
.provide_holder_witnesses(tx_signatures, &self.context.secp_ctx)
9213-
.map_err(|err| APIError::APIMisuseError { err })?;
9214-
9215-
let logger = WithChannelContext::from(logger, &self.context, None);
9216-
if tx_signatures.is_some() {
9217-
log_info!(
9218-
logger,
9219-
"Sending tx_signatures for interactive funding transaction {funding_txid_signed}"
9220-
);
9221-
}
9222-
9223-
let (splice_negotiated, splice_locked) = if let Some(funding_tx) = funding_tx.clone() {
9224-
debug_assert!(tx_signatures.is_some());
9225-
self.on_tx_signatures_exchange(funding_tx, best_block_height, &logger)
9226-
} else {
9227-
(None, None)
9228-
};
9229-
9230-
let funding = self
9231-
.pending_splice
9232-
.as_ref()
9233-
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
9234-
.and_then(|funding_negotiation| funding_negotiation.as_funding())
9235-
.unwrap_or(&self.funding);
9236-
let commitment_signed = self.context.get_initial_commitment_signed_v2(funding, &&logger);
9237-
9238-
// If we have a pending splice with a buffered initial commitment_signed from our
9239-
// counterparty, process it now that we have provided our signatures.
9240-
let counterparty_initial_commitment_signed_result = if let Some(commit_sig) = self
9241-
.pending_splice
9242-
.as_mut()
9243-
.and_then(|pending_splice| pending_splice.funding_negotiation.as_mut())
9244-
.and_then(|funding_negotiation| {
9245-
if let FundingNegotiation::AwaitingSignatures {
9246-
ref mut initial_commitment_signed_from_counterparty,
9247-
..
9248-
} = funding_negotiation
9249-
{
9250-
initial_commitment_signed_from_counterparty.take()
9251-
} else {
9252-
None
9253-
}
9254-
}) {
9255-
Some(self.splice_initial_commitment_signed(&commit_sig, fee_estimator, &&logger))
9256-
} else {
9257-
None
9258-
};
9259-
9260-
Ok(FundingTxSigned {
9261-
commitment_signed,
9262-
counterparty_initial_commitment_signed_result,
9263-
tx_signatures,
9264-
funding_tx,
9265-
splice_negotiated,
9266-
splice_locked,
9267-
})
9268-
}
9269-
92709309
pub fn tx_signatures<L: Deref>(
92719310
&mut self, msg: &msgs::TxSignatures, best_block_height: u32, logger: &L,
92729311
) -> Result<FundingTxSigned, ChannelError>

0 commit comments

Comments
 (0)