Update PendingHTLCStatus to hold malformed HTLC error messages
[rust-lightning] / src / ln / channel.rs
index a54be7722384fd2da84ed7de9c801d48e7a527be..44875fcd698089c4d8e9966b195c105fb1eebfb8 100644 (file)
@@ -16,7 +16,7 @@ use crypto::hkdf::{hkdf_extract,hkdf_expand};
 use ln::msgs;
 use ln::msgs::{ErrorAction, HandleError, MsgEncodable};
 use ln::channelmonitor::ChannelMonitor;
-use ln::channelmanager::{PendingForwardHTLCInfo, HTLCFailReason};
+use ln::channelmanager::{PendingHTLCStatus, PendingForwardHTLCInfo, HTLCFailReason, HTLCFailureMsg};
 use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
 use ln::chan_utils;
 use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
@@ -164,7 +164,7 @@ struct HTLCOutput { //TODO: Refactor into Outbound/InboundHTLCOutput (will save
        /// If we're in LocalRemoved*, set to true if we fulfilled the HTLC, and can claim money
        local_removed_fulfilled: bool,
        /// state pre-committed Remote* implies pending_forward_state, otherwise it must be None
-       pending_forward_state: Option<PendingForwardHTLCInfo>,
+       pending_forward_state: Option<PendingHTLCStatus>,
 }
 
 impl HTLCOutput {
@@ -1018,10 +1018,13 @@ impl Channel {
                for (idx, htlc) in self.pending_htlcs.iter().enumerate() {
                        if !htlc.outbound && htlc.payment_hash == payment_hash_calc &&
                                        htlc.state != HTLCState::LocalRemoved && htlc.state != HTLCState::LocalRemovedAwaitingCommitment {
-                               if pending_idx != std::usize::MAX {
-                                       panic!("Duplicate HTLC payment_hash, ChannelManager should have prevented this!");
+                               if let Some(PendingHTLCStatus::Fail(_)) = htlc.pending_forward_state {
+                               } else {
+                                       if pending_idx != std::usize::MAX {
+                                               panic!("Duplicate HTLC payment_hash, ChannelManager should have prevented this!");
+                                       }
+                                       pending_idx = idx;
                                }
-                               pending_idx = idx;
                        }
                }
                if pending_idx == std::usize::MAX {
@@ -1131,17 +1134,24 @@ impl Channel {
                                if htlc_id != 0 {
                                        panic!("Duplicate HTLC payment_hash, you probably re-used payment preimages, NEVER DO THIS!");
                                }
-                               htlc_id = htlc.htlc_id;
-                               htlc_amount_msat += htlc.amount_msat;
                                if htlc.state == HTLCState::Committed {
                                        htlc.state = HTLCState::LocalRemoved;
                                } else if htlc.state == HTLCState::RemoteAnnounced {
-                                       panic!("Somehow forwarded HTLC prior to remote revocation!");
+                                       if let Some(PendingHTLCStatus::Forward(_)) = htlc.pending_forward_state {
+                                               panic!("Somehow forwarded HTLC prior to remote revocation!");
+                                       } else {
+                                               // We have to pretend this isn't here - we're probably a duplicate with the
+                                               // same payment_hash as some other HTLC, and the other is getting failed,
+                                               // we'll fail this one as soon as remote commits to it.
+                                               continue;
+                                       }
                                } else if htlc.state == HTLCState::LocalRemoved || htlc.state == HTLCState::LocalRemovedAwaitingCommitment {
                                        return Err(HandleError{err: "Unable to find a pending HTLC which matched the given payment preimage", action: None});
                                } else {
                                        panic!("Have an inbound HTLC when not awaiting remote revoke that had a garbage state");
                                }
+                               htlc_id = htlc.htlc_id;
+                               htlc_amount_msat += htlc.amount_msat;
                        }
                }
                if htlc_amount_msat == 0 {
@@ -1381,7 +1391,7 @@ impl Channel {
                (inbound_htlc_count, outbound_htlc_count, htlc_outbound_value_msat, htlc_inbound_value_msat)
        }
 
-       pub fn update_add_htlc(&mut self, msg: &msgs::UpdateAddHTLC, pending_forward_state: PendingForwardHTLCInfo) -> Result<(), HandleError> {
+       pub fn update_add_htlc(&mut self, msg: &msgs::UpdateAddHTLC, pending_forward_state: PendingHTLCStatus) -> Result<(), HandleError> {
                if (self.channel_state & (ChannelState::ChannelFunded as u32 | ChannelState::RemoteShutdownSent as u32)) != (ChannelState::ChannelFunded as u32) {
                        return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", action: None});
                }
@@ -1633,6 +1643,7 @@ impl Channel {
                                                update_add_htlcs,
                                                update_fulfill_htlcs,
                                                update_fail_htlcs,
+                                               update_fail_malformed_htlcs: Vec::new(),
                                                commitment_signed,
                                        }, monitor_update)))
                                },
@@ -1670,6 +1681,8 @@ impl Channel {
 
                let mut to_forward_infos = Vec::new();
                let mut revoked_htlcs = Vec::new();
+               let mut update_fail_htlcs = Vec::new();
+               let mut update_fail_malformed_htlcs = Vec::new();
                let mut require_commitment = false;
                let mut value_to_self_msat_diff: i64 = 0;
                // We really shouldnt have two passes here, but retain gives a non-mutable ref (Rust bug)
@@ -1693,8 +1706,20 @@ impl Channel {
                                htlc.state = HTLCState::AwaitingAnnouncedRemoteRevoke;
                                require_commitment = true;
                        } else if htlc.state == HTLCState::AwaitingAnnouncedRemoteRevoke {
-                               htlc.state = HTLCState::Committed;
-                               to_forward_infos.push(htlc.pending_forward_state.take().unwrap());
+                               match htlc.pending_forward_state.take().unwrap() {
+                                       PendingHTLCStatus::Fail(fail_msg) => {
+                                               htlc.state = HTLCState::LocalRemoved;
+                                               require_commitment = true;
+                                               match fail_msg {
+                                                       HTLCFailureMsg::Relay(msg) => update_fail_htlcs.push(msg),
+                                                       HTLCFailureMsg::Malformed(msg) => update_fail_malformed_htlcs.push(msg),
+                                               }
+                                       },
+                                       PendingHTLCStatus::Forward(forward_info) => {
+                                               to_forward_infos.push(forward_info);
+                                               htlc.state = HTLCState::Committed;
+                                       }
+                               }
                        } else if htlc.state == HTLCState::AwaitingRemoteRevokeToRemove {
                                htlc.state = HTLCState::AwaitingRemovedRemoteRevoke;
                                require_commitment = true;
@@ -1706,7 +1731,15 @@ impl Channel {
                self.value_to_self_msat = (self.value_to_self_msat as i64 + value_to_self_msat_diff) as u64;
 
                match self.free_holding_cell_htlcs()? {
-                       Some(commitment_update) => {
+                       Some(mut commitment_update) => {
+                               commitment_update.0.update_fail_htlcs.reserve(update_fail_htlcs.len());
+                               for fail_msg in update_fail_htlcs.drain(..) {
+                                       commitment_update.0.update_fail_htlcs.push(fail_msg);
+                               }
+                               commitment_update.0.update_fail_malformed_htlcs.reserve(update_fail_malformed_htlcs.len());
+                               for fail_msg in update_fail_malformed_htlcs.drain(..) {
+                                       commitment_update.0.update_fail_malformed_htlcs.push(fail_msg);
+                               }
                                Ok((Some(commitment_update.0), to_forward_infos, revoked_htlcs, commitment_update.1))
                        },
                        None => {
@@ -1715,7 +1748,8 @@ impl Channel {
                                        Ok((Some(msgs::CommitmentUpdate {
                                                update_add_htlcs: Vec::new(),
                                                update_fulfill_htlcs: Vec::new(),
-                                               update_fail_htlcs: Vec::new(),
+                                               update_fail_htlcs,
+                                               update_fail_malformed_htlcs,
                                                commitment_signed
                                        }), to_forward_infos, revoked_htlcs, monitor_update))
                                } else {