Skip to content

Commit 04a7560

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 6f5c826 commit 04a7560

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
};
@@ -2137,6 +2145,180 @@ where
21372145
Ok(())
21382146
}
21392147

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

22082390
// We delay processing this until the user manually approves the splice via
2209-
// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2391+
// [`Channel::funding_transaction_signed`], as otherwise, there would be a
22102392
// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would
22112393
// need to undo if they no longer wish to proceed.
22122394
if has_holder_tx_signatures {
@@ -2710,7 +2892,7 @@ enum FundingNegotiation {
27102892
is_initiator: bool,
27112893
/// The initial [`msgs::CommitmentSigned`] message received for the [`FundingScope`] above.
27122894
/// We delay processing this until the user manually approves the splice via
2713-
/// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2895+
/// [`Channel::funding_transaction_signed`], as otherwise, there would be a
27142896
/// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would need to undo
27152897
/// if they no longer wish to proceed.
27162898
///
@@ -9117,149 +9299,6 @@ where
91179299
}
91189300
}
91199301

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

0 commit comments

Comments
 (0)