Skip to content

Commit be67c67

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 7e226a0 commit be67c67

File tree

2 files changed

+302
-274
lines changed

2 files changed

+302
-274
lines changed

lightning/src/ln/channel.rs

Lines changed: 186 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,11 +1954,18 @@ 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+
self.funding_transaction_signed(funding_txid, vec![], 0, fee_estimator, logger)
1958+
.map(Some)
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+
})?
19621969
} else {
19631970
None
19641971
};
@@ -2137,6 +2144,178 @@ where
21372144
Ok(())
21382145
}
21392146

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

22082387
// We delay processing this until the user manually approves the splice via
2209-
// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2388+
// [`Channel::funding_transaction_signed`], as otherwise, there would be a
22102389
// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would
22112390
// need to undo if they no longer wish to proceed.
22122391
if has_holder_tx_signatures {
@@ -2710,7 +2889,7 @@ enum FundingNegotiation {
27102889
is_initiator: bool,
27112890
/// The initial [`msgs::CommitmentSigned`] message received for the [`FundingScope`] above.
27122891
/// We delay processing this until the user manually approves the splice via
2713-
/// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2892+
/// [`Channel::funding_transaction_signed`], as otherwise, there would be a
27142893
/// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would need to undo
27152894
/// if they no longer wish to proceed.
27162895
///
@@ -9117,148 +9296,6 @@ where
91179296
}
91189297
}
91199298

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 = 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-
.map(|commit_sig| {
9249-
self.splice_initial_commitment_signed(&commit_sig, fee_estimator, &&logger)
9250-
});
9251-
9252-
Ok(FundingTxSigned {
9253-
commitment_signed,
9254-
counterparty_initial_commitment_signed_result,
9255-
tx_signatures,
9256-
funding_tx,
9257-
splice_negotiated,
9258-
splice_locked,
9259-
})
9260-
}
9261-
92629299
pub fn tx_signatures<L: Deref>(
92639300
&mut self, msg: &msgs::TxSignatures, best_block_height: u32, logger: &L,
92649301
) -> Result<FundingTxSigned, ChannelError>

0 commit comments

Comments
 (0)