Skip to content

Commit a534a63

Browse files
committed
Move commitment_signed_check to ChannelContext
Now that commitment_signed is split into check and accept methods, move the check portion from FundedChannel to ChannelContext. This allows calling it with a different FundingScope when there are pending splices and RBF attempts.
1 parent 1b419fa commit a534a63

File tree

2 files changed

+128
-124
lines changed

2 files changed

+128
-124
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
530530
);
531531

532532
/// Partial data from ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo used to simplify the
533-
/// return type of `FundedChannel::commitment_signed_check`.
533+
/// return type of `ChannelContext::commitment_signed_check`.
534534
pub(crate) struct LatestHolderCommitmentTXInfo {
535535
pub commitment_tx: HolderCommitmentTransaction,
536536
pub htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,

lightning/src/ln/channel.rs

Lines changed: 127 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -3452,6 +3452,132 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
34523452
!matches!(self.channel_state, ChannelState::AwaitingChannelReady(flags) if flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH))
34533453
}
34543454

3455+
fn validate_commitment_signed<L: Deref>(
3456+
&self, funding: &FundingScope, holder_commitment_point: &HolderCommitmentPoint,
3457+
msg: &msgs::CommitmentSigned, logger: &L,
3458+
) -> Result<LatestHolderCommitmentTXInfo, ChannelError>
3459+
where
3460+
L::Target: Logger,
3461+
{
3462+
let funding_script = funding.get_funding_redeemscript();
3463+
3464+
let keys = self.build_holder_transaction_keys(funding, holder_commitment_point.current_point());
3465+
3466+
let commitment_stats = self.build_commitment_transaction(funding, holder_commitment_point.transaction_number(), &keys, true, false, logger);
3467+
let commitment_txid = {
3468+
let trusted_tx = commitment_stats.tx.trust();
3469+
let bitcoin_tx = trusted_tx.built_transaction();
3470+
let sighash = bitcoin_tx.get_sighash_all(&funding_script, funding.get_value_satoshis());
3471+
3472+
log_trace!(logger, "Checking commitment tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}",
3473+
log_bytes!(msg.signature.serialize_compact()[..]),
3474+
log_bytes!(funding.counterparty_funding_pubkey().serialize()), encode::serialize_hex(&bitcoin_tx.transaction),
3475+
log_bytes!(sighash[..]), encode::serialize_hex(&funding_script), &self.channel_id());
3476+
if let Err(_) = self.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &funding.counterparty_funding_pubkey()) {
3477+
return Err(ChannelError::close("Invalid commitment tx signature from peer".to_owned()));
3478+
}
3479+
bitcoin_tx.txid
3480+
};
3481+
let mut htlcs_cloned: Vec<_> = commitment_stats.htlcs_included.iter().map(|htlc| (htlc.0.clone(), htlc.1.map(|h| h.clone()))).collect();
3482+
3483+
// If our counterparty updated the channel fee in this commitment transaction, check that
3484+
// they can actually afford the new fee now.
3485+
let update_fee = if let Some((_, update_state)) = self.pending_update_fee {
3486+
update_state == FeeUpdateState::RemoteAnnounced
3487+
} else { false };
3488+
if update_fee {
3489+
debug_assert!(!funding.is_outbound());
3490+
let counterparty_reserve_we_require_msat = funding.holder_selected_channel_reserve_satoshis * 1000;
3491+
if commitment_stats.remote_balance_msat < commitment_stats.total_fee_sat * 1000 + counterparty_reserve_we_require_msat {
3492+
return Err(ChannelError::close("Funding remote cannot afford proposed new fee".to_owned()));
3493+
}
3494+
}
3495+
#[cfg(any(test, fuzzing))]
3496+
{
3497+
if funding.is_outbound() {
3498+
let projected_commit_tx_info = funding.next_local_commitment_tx_fee_info_cached.lock().unwrap().take();
3499+
*funding.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
3500+
if let Some(info) = projected_commit_tx_info {
3501+
let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len()
3502+
+ self.holding_cell_htlc_updates.len();
3503+
if info.total_pending_htlcs == total_pending_htlcs
3504+
&& info.next_holder_htlc_id == self.next_holder_htlc_id
3505+
&& info.next_counterparty_htlc_id == self.next_counterparty_htlc_id
3506+
&& info.feerate == self.feerate_per_kw {
3507+
assert_eq!(commitment_stats.total_fee_sat, info.fee / 1000);
3508+
}
3509+
}
3510+
}
3511+
}
3512+
3513+
if msg.htlc_signatures.len() != commitment_stats.num_nondust_htlcs {
3514+
return Err(ChannelError::close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), commitment_stats.num_nondust_htlcs)));
3515+
}
3516+
3517+
// Up to LDK 0.0.115, HTLC information was required to be duplicated in the
3518+
// `htlcs_and_sigs` vec and in the `holder_commitment_tx` itself, both of which were passed
3519+
// in the `ChannelMonitorUpdate`. In 0.0.115, support for having a separate set of
3520+
// outbound-non-dust-HTLCSources in the `ChannelMonitorUpdate` was added, however for
3521+
// backwards compatibility, we never use it in production. To provide test coverage, here,
3522+
// we randomly decide (in test/fuzzing builds) to use the new vec sometimes.
3523+
#[allow(unused_assignments, unused_mut)]
3524+
let mut separate_nondust_htlc_sources = false;
3525+
#[cfg(all(feature = "std", any(test, fuzzing)))] {
3526+
use core::hash::{BuildHasher, Hasher};
3527+
// Get a random value using the only std API to do so - the DefaultHasher
3528+
let rand_val = std::collections::hash_map::RandomState::new().build_hasher().finish();
3529+
separate_nondust_htlc_sources = rand_val % 2 == 0;
3530+
}
3531+
3532+
let mut nondust_htlc_sources = Vec::with_capacity(htlcs_cloned.len());
3533+
let mut htlcs_and_sigs = Vec::with_capacity(htlcs_cloned.len());
3534+
for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() {
3535+
if let Some(_) = htlc.transaction_output_index {
3536+
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_stats.feerate_per_kw,
3537+
funding.get_counterparty_selected_contest_delay().unwrap(), &htlc, &self.channel_type,
3538+
&keys.broadcaster_delayed_payment_key, &keys.revocation_key);
3539+
3540+
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &self.channel_type, &keys);
3541+
let htlc_sighashtype = if self.channel_type.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
3542+
let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), htlc_sighashtype).unwrap()[..]);
3543+
log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}.",
3544+
log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.to_public_key().serialize()),
3545+
encode::serialize_hex(&htlc_tx), log_bytes!(htlc_sighash[..]), encode::serialize_hex(&htlc_redeemscript), &self.channel_id());
3546+
if let Err(_) = self.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &keys.countersignatory_htlc_key.to_public_key()) {
3547+
return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned()));
3548+
}
3549+
if !separate_nondust_htlc_sources {
3550+
htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source_opt.take()));
3551+
}
3552+
} else {
3553+
htlcs_and_sigs.push((htlc, None, source_opt.take()));
3554+
}
3555+
if separate_nondust_htlc_sources {
3556+
if let Some(source) = source_opt.take() {
3557+
nondust_htlc_sources.push(source);
3558+
}
3559+
}
3560+
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
3561+
}
3562+
3563+
let holder_commitment_tx = HolderCommitmentTransaction::new(
3564+
commitment_stats.tx,
3565+
msg.signature,
3566+
msg.htlc_signatures.clone(),
3567+
&funding.get_holder_pubkeys().funding_pubkey,
3568+
funding.counterparty_funding_pubkey()
3569+
);
3570+
3571+
self.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.outbound_htlc_preimages)
3572+
.map_err(|_| ChannelError::close("Failed to validate our commitment".to_owned()))?;
3573+
3574+
Ok(LatestHolderCommitmentTXInfo {
3575+
commitment_tx: holder_commitment_tx,
3576+
htlc_outputs: htlcs_and_sigs,
3577+
nondust_htlc_sources,
3578+
})
3579+
}
3580+
34553581
/// Transaction nomenclature is somewhat confusing here as there are many different cases - a
34563582
/// transaction is referred to as "a's transaction" implying that a will be able to broadcast
34573583
/// the transaction. Thus, b will generally be sending a signature over such a transaction to
@@ -5494,128 +5620,6 @@ impl<SP: Deref> FundedChannel<SP> where
54945620
Ok(channel_monitor)
54955621
}
54965622

5497-
fn validate_commitment_signed<L: Deref>(&self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<LatestHolderCommitmentTXInfo, ChannelError>
5498-
where L::Target: Logger
5499-
{
5500-
let funding_script = self.funding.get_funding_redeemscript();
5501-
5502-
let keys = self.context.build_holder_transaction_keys(&self.funding, self.holder_commitment_point.current_point());
5503-
5504-
let commitment_stats = self.context.build_commitment_transaction(&self.funding, self.holder_commitment_point.transaction_number(), &keys, true, false, logger);
5505-
let commitment_txid = {
5506-
let trusted_tx = commitment_stats.tx.trust();
5507-
let bitcoin_tx = trusted_tx.built_transaction();
5508-
let sighash = bitcoin_tx.get_sighash_all(&funding_script, self.funding.get_value_satoshis());
5509-
5510-
log_trace!(logger, "Checking commitment tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}",
5511-
log_bytes!(msg.signature.serialize_compact()[..]),
5512-
log_bytes!(self.funding.counterparty_funding_pubkey().serialize()), encode::serialize_hex(&bitcoin_tx.transaction),
5513-
log_bytes!(sighash[..]), encode::serialize_hex(&funding_script), &self.context.channel_id());
5514-
if let Err(_) = self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.funding.counterparty_funding_pubkey()) {
5515-
return Err(ChannelError::close("Invalid commitment tx signature from peer".to_owned()));
5516-
}
5517-
bitcoin_tx.txid
5518-
};
5519-
let mut htlcs_cloned: Vec<_> = commitment_stats.htlcs_included.iter().map(|htlc| (htlc.0.clone(), htlc.1.map(|h| h.clone()))).collect();
5520-
5521-
// If our counterparty updated the channel fee in this commitment transaction, check that
5522-
// they can actually afford the new fee now.
5523-
let update_fee = if let Some((_, update_state)) = self.context.pending_update_fee {
5524-
update_state == FeeUpdateState::RemoteAnnounced
5525-
} else { false };
5526-
if update_fee {
5527-
debug_assert!(!self.funding.is_outbound());
5528-
let counterparty_reserve_we_require_msat = self.funding.holder_selected_channel_reserve_satoshis * 1000;
5529-
if commitment_stats.remote_balance_msat < commitment_stats.total_fee_sat * 1000 + counterparty_reserve_we_require_msat {
5530-
return Err(ChannelError::close("Funding remote cannot afford proposed new fee".to_owned()));
5531-
}
5532-
}
5533-
#[cfg(any(test, fuzzing))]
5534-
{
5535-
if self.funding.is_outbound() {
5536-
let projected_commit_tx_info = self.funding.next_local_commitment_tx_fee_info_cached.lock().unwrap().take();
5537-
*self.funding.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
5538-
if let Some(info) = projected_commit_tx_info {
5539-
let total_pending_htlcs = self.context.pending_inbound_htlcs.len() + self.context.pending_outbound_htlcs.len()
5540-
+ self.context.holding_cell_htlc_updates.len();
5541-
if info.total_pending_htlcs == total_pending_htlcs
5542-
&& info.next_holder_htlc_id == self.context.next_holder_htlc_id
5543-
&& info.next_counterparty_htlc_id == self.context.next_counterparty_htlc_id
5544-
&& info.feerate == self.context.feerate_per_kw {
5545-
assert_eq!(commitment_stats.total_fee_sat, info.fee / 1000);
5546-
}
5547-
}
5548-
}
5549-
}
5550-
5551-
if msg.htlc_signatures.len() != commitment_stats.num_nondust_htlcs {
5552-
return Err(ChannelError::close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), commitment_stats.num_nondust_htlcs)));
5553-
}
5554-
5555-
// Up to LDK 0.0.115, HTLC information was required to be duplicated in the
5556-
// `htlcs_and_sigs` vec and in the `holder_commitment_tx` itself, both of which were passed
5557-
// in the `ChannelMonitorUpdate`. In 0.0.115, support for having a separate set of
5558-
// outbound-non-dust-HTLCSources in the `ChannelMonitorUpdate` was added, however for
5559-
// backwards compatibility, we never use it in production. To provide test coverage, here,
5560-
// we randomly decide (in test/fuzzing builds) to use the new vec sometimes.
5561-
#[allow(unused_assignments, unused_mut)]
5562-
let mut separate_nondust_htlc_sources = false;
5563-
#[cfg(all(feature = "std", any(test, fuzzing)))] {
5564-
use core::hash::{BuildHasher, Hasher};
5565-
// Get a random value using the only std API to do so - the DefaultHasher
5566-
let rand_val = std::collections::hash_map::RandomState::new().build_hasher().finish();
5567-
separate_nondust_htlc_sources = rand_val % 2 == 0;
5568-
}
5569-
5570-
let mut nondust_htlc_sources = Vec::with_capacity(htlcs_cloned.len());
5571-
let mut htlcs_and_sigs = Vec::with_capacity(htlcs_cloned.len());
5572-
for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() {
5573-
if let Some(_) = htlc.transaction_output_index {
5574-
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_stats.feerate_per_kw,
5575-
self.funding.get_counterparty_selected_contest_delay().unwrap(), &htlc, &self.context.channel_type,
5576-
&keys.broadcaster_delayed_payment_key, &keys.revocation_key);
5577-
5578-
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &keys);
5579-
let htlc_sighashtype = if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
5580-
let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), htlc_sighashtype).unwrap()[..]);
5581-
log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}.",
5582-
log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.to_public_key().serialize()),
5583-
encode::serialize_hex(&htlc_tx), log_bytes!(htlc_sighash[..]), encode::serialize_hex(&htlc_redeemscript), &self.context.channel_id());
5584-
if let Err(_) = self.context.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &keys.countersignatory_htlc_key.to_public_key()) {
5585-
return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned()));
5586-
}
5587-
if !separate_nondust_htlc_sources {
5588-
htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source_opt.take()));
5589-
}
5590-
} else {
5591-
htlcs_and_sigs.push((htlc, None, source_opt.take()));
5592-
}
5593-
if separate_nondust_htlc_sources {
5594-
if let Some(source) = source_opt.take() {
5595-
nondust_htlc_sources.push(source);
5596-
}
5597-
}
5598-
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
5599-
}
5600-
5601-
let holder_commitment_tx = HolderCommitmentTransaction::new(
5602-
commitment_stats.tx,
5603-
msg.signature,
5604-
msg.htlc_signatures.clone(),
5605-
&self.funding.get_holder_pubkeys().funding_pubkey,
5606-
self.funding.counterparty_funding_pubkey()
5607-
);
5608-
5609-
self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.outbound_htlc_preimages)
5610-
.map_err(|_| ChannelError::close("Failed to validate our commitment".to_owned()))?;
5611-
5612-
Ok(LatestHolderCommitmentTXInfo {
5613-
commitment_tx: holder_commitment_tx,
5614-
htlc_outputs: htlcs_and_sigs,
5615-
nondust_htlc_sources,
5616-
})
5617-
}
5618-
56195623
pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
56205624
where L::Target: Logger
56215625
{
@@ -5632,7 +5636,7 @@ impl<SP: Deref> FundedChannel<SP> where
56325636
return Err(ChannelError::close("Peer sent commitment_signed after we'd started exchanging closing_signeds".to_owned()));
56335637
}
56345638

5635-
let commitment_tx_info = self.validate_commitment_signed(msg, logger)?;
5639+
let commitment_tx_info = self.context.validate_commitment_signed(&self.funding, &self.holder_commitment_point, msg, logger)?;
56365640

56375641
// Update state now that we've passed all the can-fail calls...
56385642
let mut need_commitment = false;

0 commit comments

Comments
 (0)