Skip to content

Commit 7f289d0

Browse files
committed
Clarify update_fail/fulfill_htlc holding cell allowed Errs
Specifically, there really should be no Errs, but in case there is some case where duplicate HTLC removes are possible, return IgnoreError and debug_assert to see if fuzzing can find them.
1 parent 33acee1 commit 7f289d0

File tree

1 file changed

+54
-38
lines changed

1 file changed

+54
-38
lines changed

src/ln/channel.rs

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,8 @@ impl Channel {
10211021
Ok(our_sig)
10221022
}
10231023

1024+
/// May return an IgnoreError, but should not, and will always return Ok(_) when
1025+
/// debug_assertions are turned on
10241026
fn get_update_fulfill_htlc(&mut self, htlc_id_arg: u64, payment_preimage_arg: [u8; 32]) -> Result<(Option<msgs::UpdateFulfillHTLC>, Option<ChannelMonitor>), HandleError> {
10251027
// Either ChannelFunded got set (which means it wont bet unset) or there is no way any
10261028
// caller thought we could have something claimed (cause we wouldn't have accepted in an
@@ -1040,14 +1042,17 @@ impl Channel {
10401042
for (idx, htlc) in self.pending_inbound_htlcs.iter().enumerate() {
10411043
if htlc.htlc_id == htlc_id_arg {
10421044
assert_eq!(htlc.payment_hash, payment_hash_calc);
1043-
if htlc.state != InboundHTLCState::LocalRemoved {
1044-
pending_idx = idx;
1045-
break;
1045+
if htlc.state != InboundHTLCState::Committed {
1046+
debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to");
1047+
// Don't return in release mode here so that we can update channel_monitor
10461048
}
1049+
pending_idx = idx;
1050+
break;
10471051
}
10481052
}
10491053
if pending_idx == std::usize::MAX {
1050-
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: None});
1054+
debug_assert!(false, "Unable to find a pending HTLC which matched the given HTLC ID");
1055+
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: Some(msgs::ErrorAction::IgnoreError)});
10511056
}
10521057

10531058
// Now update local state:
@@ -1061,12 +1066,14 @@ impl Channel {
10611066
match pending_update {
10621067
&HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
10631068
if htlc_id_arg == htlc_id {
1069+
debug_assert!(false, "Tried to fulfill an HTLC we already had a pending fulfill for");
10641070
return Ok((None, None));
10651071
}
10661072
},
10671073
&HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => {
10681074
if htlc_id_arg == htlc_id {
1069-
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: None});
1075+
debug_assert!(false, "Tried to fulfill an HTLC we already had a holding-cell failure on");
1076+
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: Some(msgs::ErrorAction::IgnoreError)});
10701077
}
10711078
},
10721079
_ => {}
@@ -1080,21 +1087,12 @@ impl Channel {
10801087

10811088
{
10821089
let htlc = &mut self.pending_inbound_htlcs[pending_idx];
1083-
if htlc.state == InboundHTLCState::Committed {
1084-
htlc.state = InboundHTLCState::LocalRemoved;
1085-
htlc.local_removed_fulfilled = true;
1086-
} else if htlc.state == InboundHTLCState::RemoteAnnounced || htlc.state == InboundHTLCState::AwaitingRemoteRevokeToAnnounce || htlc.state == InboundHTLCState::AwaitingAnnouncedRemoteRevoke {
1087-
// Theoretically we can hit this if we get the preimage on an HTLC prior to us
1088-
// having forwarded it to anyone. This implies that the sender is busted as someone
1089-
// else knows the preimage, but handling this case and implementing the logic to
1090-
// take their money would be a lot of (never-tested) code to handle a case that
1091-
// hopefully never happens. Instead, we make sure we get the preimage into the
1092-
// channel_monitor and pretend we didn't just see the preimage.
1090+
if htlc.state != InboundHTLCState::Committed {
1091+
debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to");
10931092
return Ok((None, Some(self.channel_monitor.clone())));
1094-
} else {
1095-
// LocalRemoved handled in the search loop
1096-
panic!("Have an inbound HTLC when not awaiting remote revoke that had a garbage state");
10971093
}
1094+
htlc.state = InboundHTLCState::LocalRemoved;
1095+
htlc.local_removed_fulfilled = true;
10981096
}
10991097

11001098
Ok((Some(msgs::UpdateFulfillHTLC {
@@ -1115,23 +1113,42 @@ impl Channel {
11151113
}
11161114
}
11171115

1116+
/// May return an IgnoreError, but should not, and will always return Ok(_) when
1117+
/// debug_assertions are turned on
11181118
pub fn get_update_fail_htlc(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket) -> Result<Option<msgs::UpdateFailHTLC>, HandleError> {
11191119
if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
11201120
panic!("Was asked to fail an HTLC when channel was not in an operational state");
11211121
}
11221122
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
11231123

1124+
let mut pending_idx = std::usize::MAX;
1125+
for (idx, htlc) in self.pending_inbound_htlcs.iter().enumerate() {
1126+
if htlc.htlc_id == htlc_id_arg {
1127+
if htlc.state != InboundHTLCState::Committed {
1128+
debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to");
1129+
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: Some(msgs::ErrorAction::IgnoreError)});
1130+
}
1131+
pending_idx = idx;
1132+
}
1133+
}
1134+
if pending_idx == std::usize::MAX {
1135+
debug_assert!(false, "Unable to find a pending HTLC which matched the given HTLC ID");
1136+
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: Some(msgs::ErrorAction::IgnoreError)});
1137+
}
1138+
11241139
// Now update local state:
11251140
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == (ChannelState::AwaitingRemoteRevoke as u32) {
11261141
for pending_update in self.holding_cell_htlc_updates.iter() {
11271142
match pending_update {
11281143
&HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
11291144
if htlc_id_arg == htlc_id {
1130-
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: None});
1145+
debug_assert!(false, "Unable to find a pending HTLC which matched the given HTLC ID");
1146+
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: Some(msgs::ErrorAction::IgnoreError)});
11311147
}
11321148
},
11331149
&HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => {
11341150
if htlc_id_arg == htlc_id {
1151+
debug_assert!(false, "Tried to fail an HTLC that we already had a pending failure for");
11351152
return Ok(None);
11361153
}
11371154
},
@@ -1145,23 +1162,10 @@ impl Channel {
11451162
return Ok(None);
11461163
}
11471164

1148-
let mut htlc_amount_msat = 0;
1149-
for htlc in self.pending_inbound_htlcs.iter_mut() {
1150-
if htlc.htlc_id == htlc_id_arg {
1151-
if htlc.state == InboundHTLCState::Committed {
1152-
htlc.state = InboundHTLCState::LocalRemoved;
1153-
} else if htlc.state == InboundHTLCState::RemoteAnnounced {
1154-
panic!("Somehow forwarded HTLC prior to remote revocation!");
1155-
} else if htlc.state == InboundHTLCState::LocalRemoved {
1156-
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: None});
1157-
} else {
1158-
panic!("Have an inbound HTLC when not awaiting remote revoke that had a garbage state");
1159-
}
1160-
htlc_amount_msat += htlc.amount_msat;
1161-
}
1162-
}
1163-
if htlc_amount_msat == 0 {
1164-
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: None});
1165+
{
1166+
let htlc = &mut self.pending_inbound_htlcs[pending_idx];
1167+
htlc.state = InboundHTLCState::LocalRemoved;
1168+
htlc.local_removed_fulfilled = false;
11651169
}
11661170

11671171
Ok(Some(msgs::UpdateFailHTLC {
@@ -1617,15 +1621,21 @@ impl Channel {
16171621
match self.get_update_fulfill_htlc(htlc_id, payment_preimage) {
16181622
Ok(update_fulfill_msg_option) => update_fulfill_htlcs.push(update_fulfill_msg_option.0.unwrap()),
16191623
Err(e) => {
1620-
err = Some(e);
1624+
if let Some(msgs::ErrorAction::IgnoreError) = e.action {}
1625+
else {
1626+
panic!("Got a non-IgnoreError action trying to fulfill holding cell HTLC");
1627+
}
16211628
}
16221629
}
16231630
},
16241631
&HTLCUpdateAwaitingACK::FailHTLC { htlc_id, ref err_packet } => {
16251632
match self.get_update_fail_htlc(htlc_id, err_packet.clone()) {
16261633
Ok(update_fail_msg_option) => update_fail_htlcs.push(update_fail_msg_option.unwrap()),
16271634
Err(e) => {
1628-
err = Some(e);
1635+
if let Some(msgs::ErrorAction::IgnoreError) = e.action {}
1636+
else {
1637+
panic!("Got a non-IgnoreError action trying to fail holding cell HTLC");
1638+
}
16291639
}
16301640
}
16311641
},
@@ -1639,6 +1649,12 @@ impl Channel {
16391649
//fail it back the route, if its a temporary issue we can ignore it...
16401650
match err {
16411651
None => {
1652+
if update_add_htlcs.is_empty() && update_fulfill_htlcs.is_empty() && update_fail_htlcs.is_empty() {
1653+
// This should never actually happen and indicates we got some Errs back
1654+
// from update_fulfill_htlc/update_fail_htlc, but we handle it anyway in
1655+
// case there is some strange way to hit duplicate HTLC removes.
1656+
return Ok(None);
1657+
}
16421658
let (commitment_signed, monitor_update) = self.send_commitment_no_status_check()?;
16431659
Ok(Some((msgs::CommitmentUpdate {
16441660
update_add_htlcs,

0 commit comments

Comments
 (0)