Skip to content

Commit 0fc923c

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 ffeb861 commit 0fc923c

File tree

2 files changed

+273
-250
lines changed

2 files changed

+273
-250
lines changed

lightning/src/ln/channel.rs

Lines changed: 154 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,11 +1953,18 @@ where
19531953

19541954
let funding_tx_signed = if !has_local_contribution {
19551955
let funding_txid = signing_session.unsigned_tx().tx().compute_txid();
1956-
if let ChannelPhase::Funded(chan) = &mut self.phase {
1957-
chan.funding_transaction_signed(funding_txid, vec![], 0, logger).ok()
1958-
} else {
1959-
None
1960-
}
1956+
Some(self.funding_transaction_signed(funding_txid, vec![], 0, logger).map_err(
1957+
|err| {
1958+
log_error!(
1959+
logger,
1960+
"Failed signing funding transaction without local contribution: {err:?}"
1961+
);
1962+
self.fail_interactive_tx_negotiation(
1963+
AbortReason::InternalError("Signing failed"),
1964+
logger,
1965+
)
1966+
},
1967+
)?)
19611968
} else {
19621969
None
19631970
};
@@ -2143,6 +2150,146 @@ where
21432150
Ok(())
21442151
}
21452152

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

22142361
// We delay processing this until the user manually approves the splice via
2215-
// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2362+
// [`Channel::funding_transaction_signed`], as otherwise, there would be a
22162363
// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would
22172364
// need to undo if they no longer wish to proceed.
22182365
if has_holder_tx_signatures {
@@ -2716,7 +2863,7 @@ enum FundingNegotiation {
27162863
is_initiator: bool,
27172864
/// The initial [`msgs::CommitmentSigned`] message received for the [`FundingScope`] above.
27182865
/// We delay processing this until the user manually approves the splice via
2719-
/// [`FundedChannel::funding_transaction_signed`], as otherwise, there would be a
2866+
/// [`Channel::funding_transaction_signed`], as otherwise, there would be a
27202867
/// [`ChannelMonitorUpdateStep::RenegotiatedFunding`] committed that we would need to undo
27212868
/// if they no longer wish to proceed.
27222869
///
@@ -9162,124 +9309,6 @@ where
91629309
}
91639310
}
91649311

9165-
pub fn funding_transaction_signed<L: Deref>(
9166-
&mut self, funding_txid_signed: Txid, witnesses: Vec<Witness>, best_block_height: u32,
9167-
logger: &L,
9168-
) -> Result<FundingTxSigned, APIError>
9169-
where
9170-
L::Target: Logger,
9171-
{
9172-
let signing_session =
9173-
if let Some(signing_session) = self.context.interactive_tx_signing_session.as_mut() {
9174-
if let Some(pending_splice) = self.pending_splice.as_ref() {
9175-
debug_assert!(pending_splice
9176-
.funding_negotiation
9177-
.as_ref()
9178-
.map(|funding_negotiation| matches!(
9179-
funding_negotiation,
9180-
FundingNegotiation::AwaitingSignatures { .. }
9181-
))
9182-
.unwrap_or(false));
9183-
}
9184-
9185-
if signing_session.holder_tx_signatures().is_some() {
9186-
// Our `tx_signatures` either should've been the first time we processed them,
9187-
// or we're waiting for our counterparty to send theirs first.
9188-
return Ok(FundingTxSigned {
9189-
commitment_signed: None,
9190-
9191-
tx_signatures: None,
9192-
funding_tx: None,
9193-
splice_negotiated: None,
9194-
splice_locked: None,
9195-
});
9196-
}
9197-
9198-
signing_session
9199-
} else {
9200-
if Some(funding_txid_signed) == self.funding.get_funding_txid() {
9201-
// We may be handling a duplicate call and the funding was already locked so we
9202-
// no longer have the signing session present.
9203-
return Ok(FundingTxSigned {
9204-
commitment_signed: None,
9205-
tx_signatures: None,
9206-
funding_tx: None,
9207-
splice_negotiated: None,
9208-
splice_locked: None,
9209-
});
9210-
}
9211-
let err =
9212-
format!("Channel {} not expecting funding signatures", self.context.channel_id);
9213-
return Err(APIError::APIMisuseError { err });
9214-
};
9215-
9216-
let tx = signing_session.unsigned_tx().tx();
9217-
if funding_txid_signed != tx.compute_txid() {
9218-
return Err(APIError::APIMisuseError {
9219-
err: "Transaction was malleated prior to signing".to_owned(),
9220-
});
9221-
}
9222-
9223-
let shared_input_signature =
9224-
if let Some(splice_input_index) = signing_session.unsigned_tx().shared_input_index() {
9225-
let sig = match &self.context.holder_signer {
9226-
ChannelSignerType::Ecdsa(signer) => signer.sign_splice_shared_input(
9227-
&self.funding.channel_transaction_parameters,
9228-
tx,
9229-
splice_input_index as usize,
9230-
&self.context.secp_ctx,
9231-
),
9232-
#[cfg(taproot)]
9233-
ChannelSignerType::Taproot(_) => todo!(),
9234-
};
9235-
Some(sig)
9236-
} else {
9237-
None
9238-
};
9239-
debug_assert_eq!(self.pending_splice.is_some(), shared_input_signature.is_some());
9240-
9241-
let tx_signatures = msgs::TxSignatures {
9242-
channel_id: self.context.channel_id,
9243-
tx_hash: funding_txid_signed,
9244-
witnesses,
9245-
shared_input_signature,
9246-
};
9247-
let (tx_signatures, funding_tx) = signing_session
9248-
.provide_holder_witnesses(tx_signatures, &self.context.secp_ctx)
9249-
.map_err(|err| APIError::APIMisuseError { err })?;
9250-
9251-
let logger = WithChannelContext::from(logger, &self.context, None);
9252-
if tx_signatures.is_some() {
9253-
log_info!(
9254-
logger,
9255-
"Sending tx_signatures for interactive funding transaction {funding_txid_signed}"
9256-
);
9257-
}
9258-
9259-
let (splice_negotiated, splice_locked) = if let Some(funding_tx) = funding_tx.clone() {
9260-
debug_assert!(tx_signatures.is_some());
9261-
self.on_tx_signatures_exchange(funding_tx, best_block_height, &logger)
9262-
} else {
9263-
(None, None)
9264-
};
9265-
9266-
let funding = self
9267-
.pending_splice
9268-
.as_ref()
9269-
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
9270-
.and_then(|funding_negotiation| funding_negotiation.as_funding())
9271-
.unwrap_or(&self.funding);
9272-
let commitment_signed = self.context.get_initial_commitment_signed_v2(funding, &&logger);
9273-
9274-
Ok(FundingTxSigned {
9275-
commitment_signed,
9276-
tx_signatures,
9277-
funding_tx,
9278-
splice_negotiated,
9279-
splice_locked,
9280-
})
9281-
}
9282-
92839312
pub fn tx_signatures<L: Deref>(
92849313
&mut self, msg: &msgs::TxSignatures, best_block_height: u32, logger: &L,
92859314
) -> Result<FundingTxSigned, ChannelError>

0 commit comments

Comments
 (0)