Merge pull request #1199 from valentinewallace/2021-11-phantom-node
[rust-lightning] / lightning / src / ln / channelmanager.rs
index 5ba18d7e03c10d7c2718642b7eedcd6c7b1e55b4..0a0407eacffc3ce1d5b307e8f974ad57f1618369 100644 (file)
@@ -2293,48 +2293,59 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        if let &PendingHTLCRouting::Forward { ref short_channel_id, .. } = routing {
                                let id_option = channel_state.as_ref().unwrap().short_to_id.get(&short_channel_id).cloned();
                                if let Some((err, code, chan_update)) = loop {
-                                       let forwarding_id = match id_option {
+                                       let forwarding_id_opt = match id_option {
                                                None => { // unknown_next_peer
-                                                       break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+                                                       // Note that this is likely a timing oracle for detecting whether an scid is a
+                                                       // phantom.
+                                                       if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id) {
+                                                               None
+                                                       } else {
+                                                               break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+                                                       }
                                                },
-                                               Some(id) => id.clone(),
+                                               Some(id) => Some(id.clone()),
                                        };
+                                       let (chan_update_opt, forwardee_cltv_expiry_delta) = if let Some(forwarding_id) = forwarding_id_opt {
+                                               let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
+                                               // Leave channel updates as None for private channels.
+                                               let chan_update_opt = if chan.should_announce() {
+                                                       Some(self.get_channel_update_for_unicast(chan).unwrap()) } else { None };
+                                               if !chan.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
+                                                       // Note that the behavior here should be identical to the above block - we
+                                                       // should NOT reveal the existence or non-existence of a private channel if
+                                                       // we don't allow forwards outbound over them.
+                                                       break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+                                               }
 
-                                       let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
-
-                                       if !chan.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
-                                               // Note that the behavior here should be identical to the above block - we
-                                               // should NOT reveal the existence or non-existence of a private channel if
-                                               // we don't allow forwards outbound over them.
-                                               break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
-                                       }
+                                               // Note that we could technically not return an error yet here and just hope
+                                               // that the connection is reestablished or monitor updated by the time we get
+                                               // around to doing the actual forward, but better to fail early if we can and
+                                               // hopefully an attacker trying to path-trace payments cannot make this occur
+                                               // on a small/per-node/per-channel scale.
+                                               if !chan.is_live() { // channel_disabled
+                                                       break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, chan_update_opt));
+                                               }
+                                               if *amt_to_forward < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
+                                                       break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
+                                               }
+                                               let fee = amt_to_forward.checked_mul(chan.get_fee_proportional_millionths() as u64)
+                                                       .and_then(|prop_fee| { (prop_fee / 1000000)
+                                                       .checked_add(chan.get_outbound_forwarding_fee_base_msat() as u64) });
+                                               if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
+                                                       break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, chan_update_opt));
+                                               }
+                                               (chan_update_opt, chan.get_cltv_expiry_delta())
+                                       } else { (None, MIN_CLTV_EXPIRY_DELTA) };
 
-                                       // Note that we could technically not return an error yet here and just hope
-                                       // that the connection is reestablished or monitor updated by the time we get
-                                       // around to doing the actual forward, but better to fail early if we can and
-                                       // hopefully an attacker trying to path-trace payments cannot make this occur
-                                       // on a small/per-node/per-channel scale.
-                                       if !chan.is_live() { // channel_disabled
-                                               break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, Some(self.get_channel_update_for_unicast(chan).unwrap())));
-                                       }
-                                       if *amt_to_forward < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
-                                               break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, Some(self.get_channel_update_for_unicast(chan).unwrap())));
-                                       }
-                                       let fee = amt_to_forward.checked_mul(chan.get_fee_proportional_millionths() as u64)
-                                               .and_then(|prop_fee| { (prop_fee / 1000000)
-                                               .checked_add(chan.get_outbound_forwarding_fee_base_msat() as u64) });
-                                       if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
-                                               break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, Some(self.get_channel_update_for_unicast(chan).unwrap())));
-                                       }
-                                       if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + chan.get_cltv_expiry_delta() as u64 { // incorrect_cltv_expiry
-                                               break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, Some(self.get_channel_update_for_unicast(chan).unwrap())));
+                                       if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + forwardee_cltv_expiry_delta as u64 { // incorrect_cltv_expiry
+                                               break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, chan_update_opt));
                                        }
                                        let cur_height = self.best_block.read().unwrap().height() + 1;
                                        // Theoretically, channel counterparty shouldn't send us a HTLC expiring now,
                                        // but we want to be robust wrt to counterparty packet sanitization (see
                                        // HTLC_FAIL_BACK_BUFFER rationale).
                                        if msg.cltv_expiry <= cur_height + HTLC_FAIL_BACK_BUFFER as u32 { // expiry_too_soon
-                                               break Some(("CLTV expiry is too close", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
+                                               break Some(("CLTV expiry is too close", 0x1000 | 14, chan_update_opt));
                                        }
                                        if msg.cltv_expiry > cur_height + CLTV_FAR_FAR_AWAY as u32 { // expiry_too_far
                                                break Some(("CLTV expiry is too far in the future", 21, None));
@@ -2348,7 +2359,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        // but there is no need to do that, and since we're a bit conservative with our
                                        // risk threshold it just results in failing to forward payments.
                                        if (*outgoing_cltv_value) as u64 <= (cur_height + LATENCY_GRACE_PERIOD_BLOCKS) as u64 {
-                                               break Some(("Outgoing CLTV value is too soon", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
+                                               break Some(("Outgoing CLTV value is too soon", 0x1000 | 14, chan_update_opt));
                                        }
 
                                        break None;
@@ -2984,6 +2995,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
 
                let mut new_events = Vec::new();
                let mut failed_forwards = Vec::new();
+               let mut phantom_receives: Vec<(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
                let mut handle_errors = Vec::new();
                {
                        let mut channel_state_lock = self.channel_state.lock().unwrap();
@@ -2994,26 +3006,69 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        let forward_chan_id = match channel_state.short_to_id.get(&short_chan_id) {
                                                Some(chan_id) => chan_id.clone(),
                                                None => {
-                                                       failed_forwards.reserve(pending_forwards.len());
                                                        for forward_info in pending_forwards.drain(..) {
                                                                match forward_info {
-                                                                       HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info,
-                                                                                                  prev_funding_outpoint } => {
-                                                                               let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
-                                                                                       short_channel_id: prev_short_channel_id,
-                                                                                       outpoint: prev_funding_outpoint,
-                                                                                       htlc_id: prev_htlc_id,
-                                                                                       incoming_packet_shared_secret: forward_info.incoming_shared_secret,
-                                                                               });
-                                                                               failed_forwards.push((htlc_source, forward_info.payment_hash,
-                                                                                       HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() }
-                                                                               ));
-                                                                       },
+                                                                       HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
+                                                                               routing, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value },
+                                                                               prev_funding_outpoint } => {
+                                                                                       macro_rules! fail_forward {
+                                                                                               ($msg: expr, $err_code: expr, $err_data: expr) => {
+                                                                                                       {
+                                                                                                               log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg);
+                                                                                                               let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+                                                                                                                       short_channel_id: short_chan_id,
+                                                                                                                       outpoint: prev_funding_outpoint,
+                                                                                                                       htlc_id: prev_htlc_id,
+                                                                                                                       incoming_packet_shared_secret: incoming_shared_secret,
+                                                                                                               });
+                                                                                                               failed_forwards.push((htlc_source, payment_hash,
+                                                                                                                               HTLCFailReason::Reason { failure_code: $err_code, data: $err_data }
+                                                                                                               ));
+                                                                                                               continue;
+                                                                                                       }
+                                                                                               }
+                                                                                       }
+                                                                                       if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
+                                                                                               let phantom_secret_res = self.keys_manager.get_node_secret(Recipient::PhantomNode);
+                                                                                               if phantom_secret_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id) {
+                                                                                                       let shared_secret = {
+                                                                                                               let mut arr = [0; 32];
+                                                                                                               arr.copy_from_slice(&SharedSecret::new(&onion_packet.public_key.unwrap(), &phantom_secret_res.unwrap())[..]);
+                                                                                                               arr
+                                                                                                       };
+                                                                                                       let next_hop = match onion_utils::decode_next_hop(shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
+                                                                                                               Ok(res) => res,
+                                                                                                               Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
+                                                                                                                       fail_forward!(err_msg, err_code, Vec::new());
+                                                                                                               },
+                                                                                                               Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code }) => {
+                                                                                                                       fail_forward!(err_msg, err_code, Vec::new());
+                                                                                                               },
+                                                                                                       };
+                                                                                                       match next_hop {
+                                                                                                               onion_utils::Hop::Receive(hop_data) => {
+                                                                                                                       match self.construct_recv_pending_htlc_info(hop_data, shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value) {
+                                                                                                                               Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])),
+                                                                                                                               Err(ReceiveError { err_code, err_data, msg }) => fail_forward!(msg, err_code, err_data)
+                                                                                                                       }
+                                                                                                               },
+                                                                                                               _ => panic!(),
+                                                                                                       }
+                                                                                               } else {
+                                                                                                       fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new());
+                                                                                               }
+                                                                                       } else {
+                                                                                               fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new());
+                                                                                       }
+                                                                               },
                                                                        HTLCForwardInfo::FailHTLC { .. } => {
                                                                                // Channel went away before we could fail it. This implies
                                                                                // the channel is now on chain and our counterparty is
                                                                                // trying to broadcast the HTLC-Timeout, but that's their
                                                                                // problem, not ours.
+                                                                               //
+                                                                               // `fail_htlc_backwards_internal` is never called for
+                                                                               // phantom payments, so this is unreachable for them.
                                                                        }
                                                                }
                                                        }
@@ -3320,6 +3375,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                for (htlc_source, payment_hash, failure_reason) in failed_forwards.drain(..) {
                        self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), htlc_source, &payment_hash, failure_reason);
                }
+               self.forward_htlcs(&mut phantom_receives);
 
                for (counterparty_node_id, err) in handle_errors.drain(..) {
                        let _ = handle_error!(self, err, counterparty_node_id);