Skip to content

Commit e637050

Browse files
committed
Add ChannelContext::sort_{inbounds, outbounds}_by_state
This function sorts included from excluded htlcs based on their state, and runs the two passed closures on the resulting sets. To avoid mutating the outbound preimages vector in both the included and the excluded closures, this commit stops including outbound preimages for HTLCs which are included in the commitment transaction, and only includes outbound preimages for HTLCs which are not included in the commitment transaction. This seems ok from the available docs on `sign_counterparty_commitment`, but test coverage is missing.
1 parent ea4eecf commit e637050

File tree

1 file changed

+78
-62
lines changed

1 file changed

+78
-62
lines changed

lightning/src/ln/channel.rs

+78-62
Original file line numberDiff line numberDiff line change
@@ -974,10 +974,10 @@ struct HTLCStats {
974974
}
975975

976976
/// A struct gathering data on a commitment, either local or remote.
977-
struct CommitmentData<'a> {
977+
struct CommitmentData {
978978
tx: CommitmentTransaction,
979979
stats: CommitmentStats,
980-
htlcs_included: Vec<(HTLCOutputInCommitment, Option<&'a HTLCSource>)>, // the list of HTLCs (dust HTLCs *included*) which were not ignored when building the transaction
980+
htlcs_included: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>, // the list of HTLCs (dust HTLCs *included*) which were not ignored when building the transaction
981981
outbound_htlc_preimages: Vec<PaymentPreimage>, // preimages for successful offered HTLCs since last commitment
982982
inbound_htlc_preimages: Vec<PaymentPreimage>, // preimages for successful received HTLCs since last commitment
983983
}
@@ -3604,7 +3604,6 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
36043604
}
36053605
bitcoin_tx.txid
36063606
};
3607-
let mut htlcs_cloned: Vec<_> = commitment_data.htlcs_included.iter().map(|htlc| (htlc.0.clone(), htlc.1.map(|h| h.clone()))).collect();
36083607

36093608
// If our counterparty updated the channel fee in this commitment transaction, check that
36103609
// they can actually afford the new fee now.
@@ -3655,10 +3654,10 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
36553654
separate_nondust_htlc_sources = rand_val % 2 == 0;
36563655
}
36573656

3658-
let mut nondust_htlc_sources = Vec::with_capacity(htlcs_cloned.len());
3659-
let mut htlcs_and_sigs = Vec::with_capacity(htlcs_cloned.len());
3657+
let mut nondust_htlc_sources = Vec::with_capacity(commitment_data.htlcs_included.len());
3658+
let mut htlcs_and_sigs = Vec::with_capacity(commitment_data.htlcs_included.len());
36603659
let holder_keys = commitment_data.tx.trust().keys();
3661-
for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() {
3660+
for (idx, (htlc, mut source_opt)) in commitment_data.htlcs_included.into_iter().enumerate() {
36623661
if let Some(_) = htlc.transaction_output_index {
36633662
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_data.tx.feerate_per_kw(),
36643663
funding.get_counterparty_selected_contest_delay().unwrap(), &htlc, &self.channel_type,
@@ -3674,14 +3673,14 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
36743673
return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned()));
36753674
}
36763675
if !separate_nondust_htlc_sources {
3677-
htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source_opt.take()));
3676+
htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source_opt.take().map(|source| *source)));
36783677
}
36793678
} else {
3680-
htlcs_and_sigs.push((htlc, None, source_opt.take()));
3679+
htlcs_and_sigs.push((htlc, None, source_opt.take().map(|source| *source)));
36813680
}
36823681
if separate_nondust_htlc_sources {
36833682
if let Some(source) = source_opt.take() {
3684-
nondust_htlc_sources.push(source);
3683+
nondust_htlc_sources.push(*source);
36853684
}
36863685
}
36873686
debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere");
@@ -3705,6 +3704,32 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
37053704
})
37063705
}
37073706

3707+
fn sort_inbounds_by_state<I, E, L: Deref>(&self, generated_by_local: bool, mut for_each_included: I, mut for_each_excluded: E, logger: &L)
3708+
where I: FnMut(&InboundHTLCOutput), E: FnMut(&InboundHTLCOutput), L::Target: Logger,
3709+
{
3710+
self.pending_inbound_htlcs.iter().for_each(|htlc| {
3711+
if htlc.state.included_in_commitment(generated_by_local) {
3712+
for_each_included(htlc);
3713+
} else {
3714+
log_trace!(logger, " ...not including inbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, htlc.payment_hash, htlc.amount_msat, htlc.state.as_str());
3715+
for_each_excluded(htlc);
3716+
}
3717+
})
3718+
}
3719+
3720+
fn sort_outbounds_by_state<I, E, L: Deref>(&self, generated_by_local: bool, mut for_each_included: I, mut for_each_excluded: E, logger: &L)
3721+
where I: FnMut(&OutboundHTLCOutput), E: FnMut(&OutboundHTLCOutput), L::Target: Logger,
3722+
{
3723+
self.pending_outbound_htlcs.iter().for_each(|htlc| {
3724+
if htlc.state.included_in_commitment(generated_by_local) {
3725+
for_each_included(htlc);
3726+
} else {
3727+
log_trace!(logger, " ...not including outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, htlc.payment_hash, htlc.amount_msat, htlc.state.as_str());
3728+
for_each_excluded(htlc);
3729+
}
3730+
})
3731+
}
3732+
37083733
/// Generates stats on a potential commitment transaction build, without actually building the
37093734
/// commitment transaction. See `build_commitment_transaction` for further docs.
37103735
#[inline]
@@ -3744,38 +3769,31 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
37443769
}
37453770
}
37463771
}
3747-
3748-
for ref htlc in self.pending_inbound_htlcs.iter() {
3749-
if htlc.state.included_in_commitment(generated_by_local) {
3750-
count_nondust_htlc!(htlc, false);
3751-
remote_htlc_total_msat += htlc.amount_msat;
3752-
} else {
3753-
log_trace!(logger, " ...not counting inbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, htlc.payment_hash, htlc.amount_msat, htlc.state.as_str());
3754-
match htlc.state {
3755-
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_preimage)) => {
3756-
value_to_self_msat_offset += htlc.amount_msat as i64;
3757-
},
3758-
_ => {},
3759-
}
3772+
3773+
let for_each_included = |htlc: &InboundHTLCOutput| {
3774+
count_nondust_htlc!(htlc, false);
3775+
remote_htlc_total_msat += htlc.amount_msat;
3776+
};
3777+
let for_each_excluded = |htlc: &InboundHTLCOutput| {
3778+
if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_preimage)) = htlc.state {
3779+
value_to_self_msat_offset += htlc.amount_msat as i64;
37603780
}
3761-
}
3781+
};
3782+
self.sort_inbounds_by_state(generated_by_local, for_each_included, for_each_excluded, logger);
37623783

3763-
for ref htlc in self.pending_outbound_htlcs.iter() {
3764-
if htlc.state.included_in_commitment(generated_by_local) {
3765-
count_nondust_htlc!(htlc, true);
3766-
local_htlc_total_msat += htlc.amount_msat;
3767-
} else {
3768-
log_trace!(logger, " ...not counting outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, htlc.payment_hash, htlc.amount_msat, htlc.state.as_str());
3769-
match htlc.state {
3770-
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) |
3771-
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) |
3772-
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_)) => {
3773-
value_to_self_msat_offset -= htlc.amount_msat as i64;
3774-
},
3775-
_ => {},
3776-
}
3784+
let for_each_included = |htlc: &OutboundHTLCOutput| {
3785+
count_nondust_htlc!(htlc, true);
3786+
local_htlc_total_msat += htlc.amount_msat;
3787+
};
3788+
let for_each_excluded = |htlc: &OutboundHTLCOutput| {
3789+
if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) |
3790+
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) |
3791+
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_))
3792+
= htlc.state {
3793+
value_to_self_msat_offset -= htlc.amount_msat as i64;
37773794
}
3778-
}
3795+
};
3796+
self.sort_outbounds_by_state(generated_by_local, for_each_included, for_each_excluded, logger);
37793797

37803798
// # Panics
37813799
//
@@ -3844,9 +3862,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38443862
remote_balance_before_fee_anchors_msat
38453863
} = stats;
38463864

3847-
let mut included_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)> = Vec::new();
3865+
let mut included_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)> = Vec::new();
38483866
let num_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len();
3849-
let mut included_non_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)> = Vec::with_capacity(num_htlcs);
3867+
let mut included_non_dust_htlcs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)> = Vec::with_capacity(num_htlcs);
38503868

38513869
log_trace!(logger, "Building commitment transaction number {} (really {} xor {}) for channel {} for {}, generated by {} with fee {}...",
38523870
commitment_number, (INITIAL_COMMITMENT_NUMBER - commitment_number),
@@ -3880,30 +3898,30 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38803898
}
38813899

38823900
let mut inbound_htlc_preimages: Vec<PaymentPreimage> = Vec::new();
3901+
let mut outbound_htlc_preimages: Vec<PaymentPreimage> = Vec::new();
38833902

3884-
for ref htlc in self.pending_inbound_htlcs.iter() {
3885-
if htlc.state.included_in_commitment(generated_by_local) {
3886-
add_htlc_output!(htlc, false, None);
3887-
} else {
3888-
log_trace!(logger, " ...not including inbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, htlc.payment_hash, htlc.amount_msat, htlc.state.as_str());
3889-
if let Some(preimage) = htlc.state.preimage() {
3890-
inbound_htlc_preimages.push(preimage);
3891-
}
3903+
let for_each_included = |htlc: &InboundHTLCOutput| {
3904+
add_htlc_output!(htlc, false, None);
3905+
};
3906+
let for_each_excluded = |htlc: &InboundHTLCOutput| {
3907+
if let Some(preimage) = htlc.state.preimage() {
3908+
inbound_htlc_preimages.push(preimage);
38923909
}
3893-
}
3894-
3895-
let mut outbound_htlc_preimages: Vec<PaymentPreimage> = Vec::new();
3910+
};
3911+
self.sort_inbounds_by_state(generated_by_local, for_each_included, for_each_excluded, logger);
38963912

3897-
for ref htlc in self.pending_outbound_htlcs.iter() {
3913+
let for_each_included = |htlc: &OutboundHTLCOutput| {
3914+
// We box the source here because borrowed data cannot escape a closure
3915+
add_htlc_output!(htlc, true, Some(Box::new(htlc.source.clone())));
3916+
};
3917+
let for_each_excluded = |htlc: &OutboundHTLCOutput| {
3918+
// We previously included outbound preimages for both included and excluded HTLCs,
3919+
// but now only include preimages for HTLCs excluded from the commitment transaction.
38983920
if let Some(preimage) = htlc.state.preimage() {
38993921
outbound_htlc_preimages.push(preimage);
39003922
}
3901-
if htlc.state.included_in_commitment(generated_by_local) {
3902-
add_htlc_output!(htlc, true, Some(&htlc.source));
3903-
} else {
3904-
log_trace!(logger, " ...not including outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, htlc.payment_hash, htlc.amount_msat, htlc.state.as_str());
3905-
}
3906-
}
3923+
};
3924+
self.sort_outbounds_by_state(generated_by_local, for_each_included, for_each_excluded, logger);
39073925

39083926
// We MUST use saturating subs here, as the funder's balance is not guaranteed to be greater
39093927
// than or equal to the sum of `total_fee_sat` and `total_anchors_sat`.
@@ -8814,11 +8832,9 @@ impl<SP: Deref> FundedChannel<SP> where
88148832
}
88158833
self.context.resend_order = RAACommitmentOrder::RevokeAndACKFirst;
88168834

8817-
let (mut htlcs_ref, counterparty_commitment_tx) =
8835+
let (htlcs, counterparty_commitment_tx) =
88188836
self.build_commitment_no_state_update(logger);
88198837
let counterparty_commitment_txid = counterparty_commitment_tx.trust().txid();
8820-
let htlcs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)> =
8821-
htlcs_ref.drain(..).map(|(htlc, htlc_source)| (htlc, htlc_source.map(|source_ref| Box::new(source_ref.clone())))).collect();
88228838

88238839
if self.context.announcement_sigs_state == AnnouncementSigsState::MessageSent {
88248840
self.context.announcement_sigs_state = AnnouncementSigsState::Committed;
@@ -8845,7 +8861,7 @@ impl<SP: Deref> FundedChannel<SP> where
88458861
}
88468862

88478863
fn build_commitment_no_state_update<L: Deref>(&self, logger: &L)
8848-
-> (Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>, CommitmentTransaction)
8864+
-> (Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>, CommitmentTransaction)
88498865
where L::Target: Logger
88508866
{
88518867
let commitment_data = self.context.build_commitment_transaction(&self.funding,

0 commit comments

Comments
 (0)