Skip to content

Commit 39f11e6

Browse files
committed
Require all HTLC success states to hold their corresponding preimage
This allows us to DRY the code that calculates the `value_to_self_msat_offset` in `ChannelContext::build_commitment_stats`. HTLC success states have held their corresponding preimage since 0.0.105, and the release notes of 0.1 already require users running 0.0.123 and earlier to resolve their HTLCs before upgrading to 0.1. So this change is fully compatible with existing upgrade paths to the yet-to-be-shipped 0.2 release.
1 parent ea8533f commit 39f11e6

File tree

1 file changed

+67
-51
lines changed

1 file changed

+67
-51
lines changed

Diff for: lightning/src/ln/channel.rs

+67-51
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,11 @@ impl OutboundHTLCState {
364364

365365
fn preimage(&self) -> Option<PaymentPreimage> {
366366
match self {
367-
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(p)) |
368-
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(p)) |
369-
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(p)) => p.as_ref().copied(),
367+
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(preimage)) |
368+
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(preimage)) |
369+
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(preimage)) => {
370+
Some(*preimage)
371+
},
370372
_ => None,
371373
}
372374
}
@@ -375,20 +377,12 @@ impl OutboundHTLCState {
375377
#[derive(Clone)]
376378
#[cfg_attr(test, derive(Debug, PartialEq))]
377379
enum OutboundHTLCOutcome {
378-
/// LDK version 0.0.105+ will always fill in the preimage here.
379-
Success(Option<PaymentPreimage>),
380+
/// We started always filling in the preimages here in 0.0.105, and the requirement
381+
/// that the preimages always be filled in was added in 0.2.
382+
Success(PaymentPreimage),
380383
Failure(HTLCFailReason),
381384
}
382385

383-
impl From<Option<HTLCFailReason>> for OutboundHTLCOutcome {
384-
fn from(o: Option<HTLCFailReason>) -> Self {
385-
match o {
386-
None => OutboundHTLCOutcome::Success(None),
387-
Some(r) => OutboundHTLCOutcome::Failure(r)
388-
}
389-
}
390-
}
391-
392386
impl<'a> Into<Option<&'a HTLCFailReason>> for &'a OutboundHTLCOutcome {
393387
fn into(self) -> Option<&'a HTLCFailReason> {
394388
match self {
@@ -3876,7 +3870,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38763870
}
38773871
remote_htlc_total_msat += htlc.amount_msat;
38783872
} else {
3879-
if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_preimage)) = htlc.state {
3873+
if htlc.state.preimage().is_some() {
38803874
value_to_self_msat_offset += htlc.amount_msat as i64;
38813875
}
38823876
}
@@ -3889,10 +3883,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38893883
}
38903884
local_htlc_total_msat += htlc.amount_msat;
38913885
} else {
3892-
if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) |
3893-
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) |
3894-
OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_))
3895-
= htlc.state {
3886+
if htlc.state.preimage().is_some() {
38963887
value_to_self_msat_offset -= htlc.amount_msat as i64;
38973888
}
38983889
}
@@ -5801,16 +5792,18 @@ impl<SP: Deref> FundedChannel<SP> where
58015792
#[inline]
58025793
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<PaymentPreimage>, fail_reason: Option<HTLCFailReason>) -> Result<&OutboundHTLCOutput, ChannelError> {
58035794
assert!(!(check_preimage.is_some() && fail_reason.is_some()), "cannot fail while we have a preimage");
5795+
assert!(!(check_preimage.is_none() && fail_reason.is_none()), "either provide a preimage, or a failure reason");
58045796
for htlc in self.context.pending_outbound_htlcs.iter_mut() {
58055797
if htlc.htlc_id == htlc_id {
58065798
let outcome = match check_preimage {
5807-
None => fail_reason.into(),
5799+
// We checked that either check_preimage or fail_reason is set above, so we can safely unwrap here
5800+
None => OutboundHTLCOutcome::Failure(fail_reason.unwrap()),
58085801
Some(payment_preimage) => {
58095802
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
58105803
if payment_hash != htlc.payment_hash {
58115804
return Err(ChannelError::close(format!("Remote tried to fulfill HTLC ({}) with an incorrect preimage", htlc_id)));
58125805
}
5813-
OutboundHTLCOutcome::Success(Some(payment_preimage))
5806+
OutboundHTLCOutcome::Success(payment_preimage)
58145807
}
58155808
};
58165809
match htlc.state {
@@ -6014,10 +6007,10 @@ impl<SP: Deref> FundedChannel<SP> where
60146007
if let &mut OutboundHTLCState::RemoteRemoved(ref mut outcome) = &mut htlc.state {
60156008
log_trace!(logger, "Updating HTLC {} to AwaitingRemoteRevokeToRemove due to commitment_signed in channel {}.",
60166009
&htlc.payment_hash, &self.context.channel_id);
6017-
// Grab the preimage, if it exists, instead of cloning
6018-
let mut reason = OutboundHTLCOutcome::Success(None);
6010+
// Swap against a dummy variant to avoid a potentially expensive clone of `OutboundHTLCOutcome::Failure(HTLCFailReason)`
6011+
let mut reason = OutboundHTLCOutcome::Success(PaymentPreimage([0u8; 32]));
60196012
mem::swap(outcome, &mut reason);
6020-
if let OutboundHTLCOutcome::Success(Some(preimage)) = reason {
6013+
if let OutboundHTLCOutcome::Success(preimage) = reason {
60216014
// If a user (a) receives an HTLC claim using LDK 0.0.104 or before, then (b)
60226015
// upgrades to LDK 0.0.114 or later before the HTLC is fully resolved, we could
60236016
// have a `Success(None)` reason. In this case we could forget some HTLC
@@ -6435,8 +6428,8 @@ impl<SP: Deref> FundedChannel<SP> where
64356428
}
64366429
if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
64376430
log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", &htlc.payment_hash);
6438-
// Grab the preimage, if it exists, instead of cloning
6439-
let mut reason = OutboundHTLCOutcome::Success(None);
6431+
// Swap against a dummy variant to avoid a potentially expensive clone of `OutboundHTLCOutcome::Failure(HTLCFailReason)`
6432+
let mut reason = OutboundHTLCOutcome::Success(PaymentPreimage([0u8; 32]));
64406433
mem::swap(outcome, &mut reason);
64416434
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(reason);
64426435
require_commitment = true;
@@ -9014,8 +9007,8 @@ impl<SP: Deref> FundedChannel<SP> where
90149007
for htlc in self.context.pending_outbound_htlcs.iter_mut() {
90159008
if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
90169009
log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", &htlc.payment_hash);
9017-
// Grab the preimage, if it exists, instead of cloning
9018-
let mut reason = OutboundHTLCOutcome::Success(None);
9010+
// Swap against a dummy variant to avoid a potentially expensive clone of `OutboundHTLCOutcome::Failure(HTLCFailReason)`
9011+
let mut reason = OutboundHTLCOutcome::Success(PaymentPreimage([0u8; 32]));
90199012
mem::swap(outcome, &mut reason);
90209013
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(reason);
90219014
}
@@ -10640,7 +10633,9 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1064010633
}
1064110634
}
1064210635

10643-
let mut preimages: Vec<&Option<PaymentPreimage>> = vec![];
10636+
// The elements of this vector will always be `Some` starting in 0.2,
10637+
// but we still serialize the option to maintain backwards compatibility
10638+
let mut preimages: Vec<Option<&PaymentPreimage>> = vec![];
1064410639
let mut pending_outbound_skimmed_fees: Vec<Option<u64>> = Vec::new();
1064510640
let mut pending_outbound_blinding_points: Vec<Option<PublicKey>> = Vec::new();
1064610641

@@ -10667,15 +10662,15 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
1066710662
&OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref outcome) => {
1066810663
3u8.write(writer)?;
1066910664
if let OutboundHTLCOutcome::Success(preimage) = outcome {
10670-
preimages.push(preimage);
10665+
preimages.push(Some(preimage));
1067110666
}
1067210667
let reason: Option<&HTLCFailReason> = outcome.into();
1067310668
reason.write(writer)?;
1067410669
}
1067510670
&OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) => {
1067610671
4u8.write(writer)?;
1067710672
if let OutboundHTLCOutcome::Success(preimage) = outcome {
10678-
preimages.push(preimage);
10673+
preimages.push(Some(preimage));
1067910674
}
1068010675
let reason: Option<&HTLCFailReason> = outcome.into();
1068110676
reason.write(writer)?;
@@ -11014,15 +11009,30 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1101411009
1 => OutboundHTLCState::Committed,
1101511010
2 => {
1101611011
let option: Option<HTLCFailReason> = Readable::read(reader)?;
11017-
OutboundHTLCState::RemoteRemoved(option.into())
11012+
let outcome = match option {
11013+
Some(r) => OutboundHTLCOutcome::Failure(r),
11014+
// Initialize this variant with a dummy preimage, the actual preimage will be filled in further down
11015+
None => OutboundHTLCOutcome::Success(PaymentPreimage([0u8; 32])),
11016+
};
11017+
OutboundHTLCState::RemoteRemoved(outcome)
1101811018
},
1101911019
3 => {
1102011020
let option: Option<HTLCFailReason> = Readable::read(reader)?;
11021-
OutboundHTLCState::AwaitingRemoteRevokeToRemove(option.into())
11021+
let outcome = match option {
11022+
Some(r) => OutboundHTLCOutcome::Failure(r),
11023+
// Initialize this variant with a dummy preimage, the actual preimage will be filled in further down
11024+
None => OutboundHTLCOutcome::Success(PaymentPreimage([0u8; 32])),
11025+
};
11026+
OutboundHTLCState::AwaitingRemoteRevokeToRemove(outcome)
1102211027
},
1102311028
4 => {
1102411029
let option: Option<HTLCFailReason> = Readable::read(reader)?;
11025-
OutboundHTLCState::AwaitingRemovedRemoteRevoke(option.into())
11030+
let outcome = match option {
11031+
Some(r) => OutboundHTLCOutcome::Failure(r),
11032+
// Initialize this variant with a dummy preimage, the actual preimage will be filled in further down
11033+
None => OutboundHTLCOutcome::Success(PaymentPreimage([0u8; 32])),
11034+
};
11035+
OutboundHTLCState::AwaitingRemovedRemoteRevoke(outcome)
1102611036
},
1102711037
_ => return Err(DecodeError::InvalidValue),
1102811038
},
@@ -11169,7 +11179,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1116911179
// only, so we default to that if none was written.
1117011180
let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
1117111181
let mut channel_creation_height = 0u32;
11172-
let mut preimages_opt: Option<Vec<Option<PaymentPreimage>>> = None;
11182+
// Starting in 0.2, all the elements in this vector will be `Some`, but they are still
11183+
// serialized as options to maintain backwards compatibility
11184+
let mut preimages: Vec<Option<PaymentPreimage>> = Vec::new();
1117311185

1117411186
// If we read an old Channel, for simplicity we just treat it as "we never sent an
1117511187
// AnnouncementSignatures" which implies we'll re-send it on reconnect, but that's fine.
@@ -11223,7 +11235,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1122311235
(10, monitor_pending_update_adds, option), // Added in 0.0.122
1122411236
(11, monitor_pending_finalized_fulfills, optional_vec),
1122511237
(13, channel_creation_height, required),
11226-
(15, preimages_opt, optional_vec),
11238+
(15, preimages, required_vec), // The preimages transitioned from optional to required in 0.2
1122711239
(17, announcement_sigs_state, required),
1122811240
(19, latest_inbound_scid_alias, option),
1122911241
(21, outbound_scid_alias, required),
@@ -11251,23 +11263,27 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1125111263

1125211264
let holder_signer = signer_provider.derive_channel_signer(channel_keys_id);
1125311265

11254-
if let Some(preimages) = preimages_opt {
11255-
let mut iter = preimages.into_iter();
11256-
for htlc in pending_outbound_htlcs.iter_mut() {
11257-
match &htlc.state {
11258-
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(None)) => {
11259-
htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(iter.next().ok_or(DecodeError::InvalidValue)?));
11260-
}
11261-
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(None)) => {
11262-
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(iter.next().ok_or(DecodeError::InvalidValue)?));
11263-
}
11264-
_ => {}
11266+
let mut iter = preimages.into_iter();
11267+
for htlc in pending_outbound_htlcs.iter_mut() {
11268+
match &mut htlc.state {
11269+
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(ref mut preimage)) => {
11270+
// This variant was initialized like this further above
11271+
debug_assert_eq!(preimage, &PaymentPreimage([0u8; 32]));
11272+
// Flatten and unwrap the preimage; they are always set starting in 0.2.
11273+
*preimage = iter.next().flatten().ok_or(DecodeError::InvalidValue)?;
1126511274
}
11275+
OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(ref mut preimage)) => {
11276+
// This variant was initialized like this further above
11277+
debug_assert_eq!(preimage, &PaymentPreimage([0u8; 32]));
11278+
// Flatten and unwrap the preimage; they are always set starting in 0.2.
11279+
*preimage = iter.next().flatten().ok_or(DecodeError::InvalidValue)?;
11280+
}
11281+
_ => {}
1126611282
}
11267-
// We expect all preimages to be consumed above
11268-
if iter.next().is_some() {
11269-
return Err(DecodeError::InvalidValue);
11270-
}
11283+
}
11284+
// We expect all preimages to be consumed above
11285+
if iter.next().is_some() {
11286+
return Err(DecodeError::InvalidValue);
1127111287
}
1127211288

1127311289
let chan_features = channel_type.unwrap();

0 commit comments

Comments
 (0)