Skip to content

Commit 4ce828e

Browse files
committed
Introduce LatestHolderCommitment monitor update variant
This new variant is a backwards-incompatible successor to `LatestHolderCommitmentTXInfo` that is capable of handling holder commitment updates while a splice is pending. Since all holder commitment candidates share the same set of non-dust and dust HTLCs (though each non-dust HTLC can have differing output indices), we can share the same set of HTLC source data across all candidates.
1 parent 180b53d commit 4ce828e

File tree

2 files changed

+231
-112
lines changed

2 files changed

+231
-112
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 145 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,6 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
623623

624624
#[derive(Clone, Debug, PartialEq, Eq)]
625625
pub(crate) enum ChannelMonitorUpdateStep {
626-
// Update LatestHolderCommitmentTXInfo in channel.rs if adding new fields to this variant.
627626
LatestHolderCommitmentTXInfo {
628627
commitment_tx: HolderCommitmentTransaction,
629628
/// Note that LDK after 0.0.115 supports this only containing dust HTLCs (implying the
@@ -636,6 +635,11 @@ pub(crate) enum ChannelMonitorUpdateStep {
636635
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
637636
nondust_htlc_sources: Vec<HTLCSource>,
638637
},
638+
LatestHolderCommitment {
639+
commitment_txs: Vec<HolderCommitmentTransaction>,
640+
htlc_data: CommitmentHTLCData,
641+
claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
642+
},
639643
LatestCounterpartyCommitmentTXInfo {
640644
commitment_txid: Txid,
641645
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
@@ -683,6 +687,7 @@ impl ChannelMonitorUpdateStep {
683687
fn variant_name(&self) -> &'static str {
684688
match self {
685689
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { .. } => "LatestHolderCommitmentTXInfo",
690+
ChannelMonitorUpdateStep::LatestHolderCommitment { .. } => "LatestHolderCommitment",
686691
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. } => "LatestCounterpartyCommitmentTXInfo",
687692
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. } => "LatestCounterpartyCommitmentTX",
688693
ChannelMonitorUpdateStep::PaymentPreimage { .. } => "PaymentPreimage",
@@ -728,6 +733,11 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
728733
(0, htlc_outputs, required_vec),
729734
(2, commitment_tx, required),
730735
},
736+
(8, LatestHolderCommitment) => {
737+
(1, commitment_txs, required_vec),
738+
(3, htlc_data, required),
739+
(5, claimed_htlcs, required_vec),
740+
},
731741
(10, RenegotiatedFunding) => {
732742
(1, channel_parameters, (required: ReadableArgs, None)),
733743
(3, holder_commitment_tx, required),
@@ -988,12 +998,12 @@ where
988998
}
989999
}
9901000

991-
#[derive(Clone, PartialEq)]
992-
struct CommitmentHTLCData {
1001+
#[derive(Clone, Debug, PartialEq, Eq)]
1002+
pub(crate) struct CommitmentHTLCData {
9931003
// These must be sorted in increasing output index order to match the expected order of the
9941004
// HTLCs in the `CommitmentTransaction`.
995-
nondust_htlc_sources: Vec<HTLCSource>,
996-
dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
1005+
pub nondust_htlc_sources: Vec<HTLCSource>,
1006+
pub dust_htlcs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>,
9971007
}
9981008

9991009
impl CommitmentHTLCData {
@@ -1002,6 +1012,11 @@ impl CommitmentHTLCData {
10021012
}
10031013
}
10041014

1015+
impl_writeable_tlv_based!(CommitmentHTLCData, {
1016+
(1, nondust_htlc_sources, required_vec),
1017+
(3, dust_htlcs, required_vec),
1018+
});
1019+
10051020
impl TryFrom<HolderSignedTx> for CommitmentHTLCData {
10061021
type Error = ();
10071022
#[rustfmt::skip]
@@ -3309,11 +3324,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33093324
/// Panics if set_on_holder_tx_csv has never been called.
33103325
#[rustfmt::skip]
33113326
fn provide_latest_holder_commitment_tx(
3312-
&mut self, mut holder_commitment_tx: HolderCommitmentTransaction,
3313-
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
3327+
&mut self, holder_commitment_tx: HolderCommitmentTransaction,
3328+
htlc_outputs: &[(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)],
33143329
claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], mut nondust_htlc_sources: Vec<HTLCSource>,
3315-
) {
3316-
let dust_htlcs: Vec<_> = if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
3330+
) -> Result<(), &'static str> {
3331+
let dust_htlcs = if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
33173332
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
33183333
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
33193334
// and just pass in source data via `nondust_htlc_sources`.
@@ -3329,19 +3344,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33293344
// Backfill the non-dust HTLC sources.
33303345
debug_assert!(nondust_htlc_sources.is_empty());
33313346
nondust_htlc_sources.reserve_exact(holder_commitment_tx.nondust_htlcs().len());
3332-
let dust_htlcs = htlc_outputs.into_iter().filter_map(|(htlc, _, source)| {
3347+
htlc_outputs.iter().filter_map(|(htlc, _, source)| {
33333348
// Filter our non-dust HTLCs, while at the same time pushing their sources into
33343349
// `nondust_htlc_sources`.
33353350
if htlc.transaction_output_index.is_none() {
3336-
return Some((htlc, source));
3351+
return Some((htlc.clone(), source.clone()));
33373352
}
33383353
if htlc.offered {
3339-
nondust_htlc_sources.push(source.expect("Outbound HTLCs should have a source"));
3354+
nondust_htlc_sources.push(source.clone().expect("Outbound HTLCs should have a source"));
33403355
}
33413356
None
3342-
}).collect();
3343-
3344-
dust_htlcs
3357+
}).collect()
33453358
} else {
33463359
// If we don't have any non-dust HTLCs in htlc_outputs, assume they were all passed via
33473360
// `nondust_htlc_sources`, building up the final htlc_outputs by combining
@@ -3368,30 +3381,112 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33683381
assert!(sources.next().is_none(), "All HTLC sources should have been exhausted");
33693382

33703383
// This only includes dust HTLCs as checked above.
3371-
htlc_outputs.into_iter().map(|(htlc, _, source)| (htlc, source)).collect()
3384+
htlc_outputs.iter().map(|(htlc, _, source)| (htlc.clone(), source.clone())).collect()
33723385
};
33733386

3374-
self.current_holder_commitment_number = holder_commitment_tx.trust().commitment_number();
3375-
self.onchain_tx_handler.provide_latest_holder_tx(holder_commitment_tx.clone());
3387+
let htlc_data = CommitmentHTLCData { nondust_htlc_sources, dust_htlcs };
3388+
self.update_holder_commitment_data(
3389+
vec![holder_commitment_tx], htlc_data, claimed_htlcs.to_vec(),
3390+
)
3391+
}
3392+
3393+
fn verify_matching_commitment_transactions<
3394+
'a,
3395+
I: Iterator<Item = &'a CommitmentTransaction> + Clone,
3396+
>(
3397+
&self, commitment_txs: I,
3398+
) -> Result<(), &'static str> {
3399+
if self.pending_funding.len() + 1 != commitment_txs.clone().count() {
3400+
return Err("Commitment transaction count mismatch");
3401+
}
3402+
3403+
let mut other_commitment_tx = None::<&CommitmentTransaction>;
3404+
for (funding, commitment_tx) in
3405+
core::iter::once(&self.funding).chain(self.pending_funding.iter()).zip(commitment_txs)
3406+
{
3407+
let trusted_tx = &commitment_tx.trust().built_transaction().transaction;
3408+
if trusted_tx.input.len() != 1 {
3409+
return Err("Commitment transactions must only spend one input");
3410+
}
3411+
let funding_outpoint_spent = trusted_tx.input[0].previous_output;
3412+
if funding_outpoint_spent != funding.funding_outpoint().into_bitcoin_outpoint() {
3413+
return Err("Commitment transaction spends invalid funding outpoint");
3414+
}
3415+
3416+
if let Some(other_commitment_tx) = other_commitment_tx {
3417+
if commitment_tx.commitment_number() != other_commitment_tx.commitment_number() {
3418+
return Err("Commitment number mismatch");
3419+
}
3420+
if commitment_tx.per_commitment_point()
3421+
!= other_commitment_tx.per_commitment_point()
3422+
{
3423+
return Err("Per-commitment-point mismatch");
3424+
}
3425+
if commitment_tx.feerate_per_kw() != other_commitment_tx.feerate_per_kw() {
3426+
return Err("Commitment fee rate mismatch");
3427+
}
3428+
let nondust_htlcs = commitment_tx.nondust_htlcs();
3429+
let other_nondust_htlcs = other_commitment_tx.nondust_htlcs();
3430+
if nondust_htlcs.len() != other_nondust_htlcs.len() {
3431+
return Err("Non-dust HTLC count mismatch");
3432+
}
3433+
for (nondust_htlc, other_nondust_htlc) in
3434+
nondust_htlcs.iter().zip(other_nondust_htlcs.iter())
3435+
{
3436+
if !nondust_htlc.is_data_equal(other_nondust_htlc) {
3437+
return Err("Non-dust HTLC mismatch");
3438+
}
3439+
}
3440+
}
3441+
3442+
other_commitment_tx = Some(commitment_tx);
3443+
}
3444+
3445+
Ok(())
3446+
}
3447+
3448+
fn update_holder_commitment_data(
3449+
&mut self, commitment_txs: Vec<HolderCommitmentTransaction>,
3450+
mut htlc_data: CommitmentHTLCData, claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>,
3451+
) -> Result<(), &'static str> {
3452+
self.verify_matching_commitment_transactions(
3453+
commitment_txs.iter().map(|holder_commitment_tx| holder_commitment_tx.deref()),
3454+
)?;
33763455

3377-
mem::swap(&mut holder_commitment_tx, &mut self.funding.current_holder_commitment_tx);
3378-
self.funding.prev_holder_commitment_tx = Some(holder_commitment_tx);
3379-
let mut holder_htlc_data = CommitmentHTLCData { nondust_htlc_sources, dust_htlcs };
3380-
mem::swap(&mut holder_htlc_data, &mut self.current_holder_htlc_data);
3381-
self.prev_holder_htlc_data = Some(holder_htlc_data);
3456+
let current_funding_commitment_tx = commitment_txs.first().unwrap();
3457+
self.current_holder_commitment_number = current_funding_commitment_tx.commitment_number();
3458+
self.onchain_tx_handler.provide_latest_holder_tx(current_funding_commitment_tx.clone());
3459+
for (funding, mut commitment_tx) in core::iter::once(&mut self.funding)
3460+
.chain(self.pending_funding.iter_mut())
3461+
.zip(commitment_txs.into_iter())
3462+
{
3463+
mem::swap(&mut commitment_tx, &mut funding.current_holder_commitment_tx);
3464+
funding.prev_holder_commitment_tx = Some(commitment_tx);
3465+
}
3466+
3467+
mem::swap(&mut htlc_data, &mut self.current_holder_htlc_data);
3468+
self.prev_holder_htlc_data = Some(htlc_data);
33823469

33833470
for (claimed_htlc_id, claimed_preimage) in claimed_htlcs {
3384-
#[cfg(debug_assertions)] {
3385-
let cur_counterparty_htlcs = self.funding.counterparty_claimable_outpoints.get(
3386-
&self.funding.current_counterparty_commitment_txid.unwrap()).unwrap();
3471+
#[cfg(debug_assertions)]
3472+
{
3473+
let cur_counterparty_htlcs = self
3474+
.funding
3475+
.counterparty_claimable_outpoints
3476+
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3477+
.unwrap();
33873478
assert!(cur_counterparty_htlcs.iter().any(|(_, source_opt)| {
33883479
if let Some(source) = source_opt {
3389-
SentHTLCId::from_source(source) == *claimed_htlc_id
3390-
} else { false }
3480+
SentHTLCId::from_source(source) == claimed_htlc_id
3481+
} else {
3482+
false
3483+
}
33913484
}));
33923485
}
3393-
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
3486+
self.counterparty_fulfilled_htlcs.insert(claimed_htlc_id, claimed_preimage);
33943487
}
3488+
3489+
Ok(())
33953490
}
33963491

33973492
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
@@ -3734,9 +3829,27 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37343829
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
37353830
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
37363831
if self.lockdown_from_offchain { panic!(); }
3737-
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
3832+
if let Err(e) = self.provide_latest_holder_commitment_tx(
3833+
commitment_tx.clone(), htlc_outputs, &claimed_htlcs,
3834+
nondust_htlc_sources.clone()
3835+
) {
3836+
log_error!(logger, "Failed updating latest holder commitment transaction info: {}", e);
3837+
ret = Err(());
3838+
}
37383839
}
3739-
// Soon we will drop the `LatestCounterpartyCommitmentTXInfo` variant in favor of `LatestCounterpartyCommitmentTX`.
3840+
ChannelMonitorUpdateStep::LatestHolderCommitment {
3841+
commitment_txs, htlc_data, claimed_htlcs,
3842+
} => {
3843+
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment");
3844+
assert!(!self.lockdown_from_offchain);
3845+
if let Err(e) = self.update_holder_commitment_data(
3846+
commitment_txs.clone(), htlc_data.clone(), claimed_htlcs.clone(),
3847+
) {
3848+
log_error!(logger, "Failed updating latest holder commitment state: {}", e);
3849+
ret = Err(());
3850+
}
3851+
},
3852+
// Soon we will drop the `LatestCounterpartyCommitmentTXInfo` variant in favor of `LatestCounterpartyCommitment`.
37403853
// For now we just add the code to handle the new updates.
37413854
// Next step: in channel, switch channel monitor updates to use the `LatestCounterpartyCommitmentTX` variant.
37423855
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
@@ -3819,6 +3932,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38193932
for update in updates.updates.iter() {
38203933
match update {
38213934
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { .. }
3935+
|ChannelMonitorUpdateStep::LatestHolderCommitment { .. }
38223936
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. }
38233937
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. }
38243938
|ChannelMonitorUpdateStep::ShutdownScript { .. }

0 commit comments

Comments
 (0)