From: Matt Corallo Date: Tue, 21 Aug 2018 20:57:41 +0000 (-0400) Subject: Do not return UpdateFailHTLC updates until the HTLC is committed X-Git-Tag: v0.0.12~341^2~1 X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=6e50a84f14c0effd78760e167a4ea9217e17c92a;p=rust-lightning Do not return UpdateFailHTLC updates until the HTLC is committed This fixes a violation of BOLT 2 and will let us consolidate some HTLC update handling. Good bit of code movement, but is mostly refactor to store HTLC failure status in pending_htlcs in Channel. --- diff --git a/fuzz/fuzz_targets/channel_target.rs b/fuzz/fuzz_targets/channel_target.rs index 65f0419f5..37cf11a8c 100644 --- a/fuzz/fuzz_targets/channel_target.rs +++ b/fuzz/fuzz_targets/channel_target.rs @@ -8,7 +8,7 @@ use bitcoin::util::hash::Sha256dHash; use bitcoin::network::serialize::{serialize, BitcoinHash}; use lightning::ln::channel::{Channel, ChannelKeys}; -use lightning::ln::channelmanager::{HTLCFailReason, PendingForwardHTLCInfo}; +use lightning::ln::channelmanager::{HTLCFailReason, PendingHTLCStatus}; use lightning::ln::msgs; use lightning::ln::msgs::{MsgDecodable, ErrorAction}; use lightning::chain::chaininterface::{FeeEstimator, ConfirmationTarget}; @@ -280,7 +280,7 @@ pub fn do_test(data: &[u8]) { }, 2 => { let update_add_htlc = decode_msg!(msgs::UpdateAddHTLC, 32+8+8+32+4+4+33+20*65+32); - test_err!(channel.update_add_htlc(&update_add_htlc, PendingForwardHTLCInfo::dummy())); + test_err!(channel.update_add_htlc(&update_add_htlc, PendingHTLCStatus::dummy())); }, 3 => { let update_fulfill_htlc = decode_msg!(msgs::UpdateFulfillHTLC, 32 + 8 + 32); diff --git a/src/ln/channel.rs b/src/ln/channel.rs index a54be7722..7d858555f 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -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}; 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, + pending_forward_state: Option, } impl HTLCOutput { @@ -1381,7 +1381,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}); } @@ -1670,6 +1670,7 @@ impl Channel { let mut to_forward_infos = Vec::new(); let mut revoked_htlcs = Vec::new(); + let mut failed_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 +1694,17 @@ 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; + failed_htlcs.push(fail_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 +1716,11 @@ 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(failed_htlcs.len()); + for fail_msg in failed_htlcs.drain(..) { + commitment_update.0.update_fail_htlcs.push(fail_msg); + } Ok((Some(commitment_update.0), to_forward_infos, revoked_htlcs, commitment_update.1)) }, None => { @@ -1715,7 +1729,7 @@ impl Channel { Ok((Some(msgs::CommitmentUpdate { update_add_htlcs: Vec::new(), update_fulfill_htlcs: Vec::new(), - update_fail_htlcs: Vec::new(), + update_fail_htlcs: failed_htlcs, commitment_signed }), to_forward_infos, revoked_htlcs, monitor_update)) } else { diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index 76c7bb18f..12b784caa 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -40,6 +40,7 @@ mod channel_held_info { use ln::msgs; /// Stores the info we will need to send when we want to forward an HTLC onwards + #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug pub struct PendingForwardHTLCInfo { pub(super) onion_packet: Option, pub(super) payment_hash: [u8; 32], @@ -49,17 +50,24 @@ mod channel_held_info { pub(super) outgoing_cltv_value: u32, } + /// Stores whether we can't forward an HTLC or relevant forwarding info + #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug + pub enum PendingHTLCStatus { + Forward(PendingForwardHTLCInfo), + Fail(msgs::UpdateFailHTLC), + } + #[cfg(feature = "fuzztarget")] - impl PendingForwardHTLCInfo { + impl PendingHTLCStatus { pub fn dummy() -> Self { - Self { + PendingHTLCStatus::Forward(PendingForwardHTLCInfo { onion_packet: None, payment_hash: [0; 32], short_channel_id: 0, prev_short_channel_id: 0, amt_to_forward: 0, outgoing_cltv_value: 0, - } + }) } } @@ -667,6 +675,180 @@ impl ChannelManager { ChannelManager::encrypt_failure_packet(shared_secret, &failure_packet.encode()[..]) } + fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, SharedSecret, MutexGuard) { + let shared_secret = SharedSecret::new(&self.secp_ctx, &msg.onion_routing_packet.public_key, &self.our_network_key); + let (rho, mu) = ChannelManager::gen_rho_mu_from_shared_secret(&shared_secret); + + macro_rules! get_onion_hash { + () => { + { + let mut sha = Sha256::new(); + sha.input(&msg.onion_routing_packet.hop_data); + let mut onion_hash = [0; 32]; + sha.result(&mut onion_hash); + onion_hash + } + } + } + + let mut channel_state = None; + macro_rules! return_err { + ($msg: expr, $err_code: expr, $data: expr) => { + { + log_info!(self, "Failed to accept/forward incoming HTLC: {}", $msg); + if channel_state.is_none() { + channel_state = Some(self.channel_state.lock().unwrap()); + } + return (PendingHTLCStatus::Fail(msgs::UpdateFailHTLC { + channel_id: msg.channel_id, + htlc_id: msg.htlc_id, + reason: ChannelManager::build_first_hop_failure_packet(&shared_secret, $err_code, $data), + }), shared_secret, channel_state.unwrap()); + } + } + } + + if msg.onion_routing_packet.version != 0 { + //TODO: Spec doesn't indicate if we should only hash hop_data here (and in other + //sha256_of_onion error data packets), or the entire onion_routing_packet. Either way, + //the hash doesn't really serve any purpuse - in the case of hashing all data, the + //receiving node would have to brute force to figure out which version was put in the + //packet by the node that send us the message, in the case of hashing the hop_data, the + //node knows the HMAC matched, so they already know what is there... + return_err!("Unknown onion packet version", 0x8000 | 0x4000 | 4, &get_onion_hash!()); + } + + let mut hmac = Hmac::new(Sha256::new(), &mu); + hmac.input(&msg.onion_routing_packet.hop_data); + hmac.input(&msg.payment_hash); + if hmac.result() != MacResult::new(&msg.onion_routing_packet.hmac) { + return_err!("HMAC Check failed", 0x8000 | 0x4000 | 5, &get_onion_hash!()); + } + + let mut chacha = ChaCha20::new(&rho, &[0u8; 8]); + let next_hop_data = { + let mut decoded = [0; 65]; + chacha.process(&msg.onion_routing_packet.hop_data[0..65], &mut decoded); + match msgs::OnionHopData::decode(&decoded[..]) { + Err(err) => { + let error_code = match err { + msgs::DecodeError::UnknownRealmByte => 0x4000 | 1, + _ => 0x2000 | 2, // Should never happen + }; + return_err!("Unable to decode our hop data", error_code, &[0;0]); + }, + Ok(msg) => msg + } + }; + + //TODO: Check that msg.cltv_expiry is within acceptable bounds! + + let pending_forward_info = if next_hop_data.hmac == [0; 32] { + // OUR PAYMENT! + if next_hop_data.data.amt_to_forward != msg.amount_msat { + return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat)); + } + if next_hop_data.data.outgoing_cltv_value != msg.cltv_expiry { + return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry)); + } + + // Note that we could obviously respond immediately with an update_fulfill_htlc + // message, however that would leak that we are the recipient of this payment, so + // instead we stay symmetric with the forwarding case, only responding (after a + // delay) once they've send us a commitment_signed! + + PendingHTLCStatus::Forward(PendingForwardHTLCInfo { + onion_packet: None, + payment_hash: msg.payment_hash.clone(), + short_channel_id: 0, + prev_short_channel_id: 0, + amt_to_forward: next_hop_data.data.amt_to_forward, + outgoing_cltv_value: next_hop_data.data.outgoing_cltv_value, + }) + } else { + let mut new_packet_data = [0; 20*65]; + chacha.process(&msg.onion_routing_packet.hop_data[65..], &mut new_packet_data[0..19*65]); + chacha.process(&ChannelManager::ZERO[0..65], &mut new_packet_data[19*65..]); + + let mut new_pubkey = msg.onion_routing_packet.public_key.clone(); + + let blinding_factor = { + let mut sha = Sha256::new(); + sha.input(&new_pubkey.serialize()[..]); + sha.input(&shared_secret[..]); + let mut res = [0u8; 32]; + sha.result(&mut res); + match SecretKey::from_slice(&self.secp_ctx, &res) { + Err(_) => { + // Return temporary node failure as its technically our issue, not the + // channel's issue. + return_err!("Blinding factor is an invalid private key", 0x2000 | 2, &[0;0]); + }, + Ok(key) => key + } + }; + + match new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor) { + Err(_) => { + // Return temporary node failure as its technically our issue, not the + // channel's issue. + return_err!("New blinding factor is an invalid private key", 0x2000 | 2, &[0;0]); + }, + Ok(_) => {} + }; + + let outgoing_packet = msgs::OnionPacket { + version: 0, + public_key: new_pubkey, + hop_data: new_packet_data, + hmac: next_hop_data.hmac.clone(), + }; + + PendingHTLCStatus::Forward(PendingForwardHTLCInfo { + onion_packet: Some(outgoing_packet), + payment_hash: msg.payment_hash.clone(), + short_channel_id: next_hop_data.data.short_channel_id, + prev_short_channel_id: 0, + amt_to_forward: next_hop_data.data.amt_to_forward, + outgoing_cltv_value: next_hop_data.data.outgoing_cltv_value, + }) + }; + + channel_state = Some(self.channel_state.lock().unwrap()); + if let &PendingHTLCStatus::Forward(PendingForwardHTLCInfo { ref onion_packet, ref short_channel_id, ref amt_to_forward, ref outgoing_cltv_value, .. }) = &pending_forward_info { + if onion_packet.is_some() { // If short_channel_id is 0 here, we'll reject them in the body here + let id_option = channel_state.as_ref().unwrap().short_to_id.get(&short_channel_id).cloned(); + let forwarding_id = match id_option { + None => { + return_err!("Don't have available channel for forwarding as requested.", 0x4000 | 10, &[0;0]); + }, + Some(id) => id.clone(), + }; + if let Some((err, code, chan_update)) = { + let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap(); + if !chan.is_live() { + Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, self.get_channel_update(chan).unwrap())) + } else { + let fee = chan.get_our_fee_base_msat(&*self.fee_estimator) + (*amt_to_forward * self.fee_proportional_millionths as u64 / 1000000) as u32; + if msg.amount_msat < fee as u64 || (msg.amount_msat - fee as u64) < *amt_to_forward { + Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, self.get_channel_update(chan).unwrap())) + } else { + if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + CLTV_EXPIRY_DELTA as u64 { + Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, self.get_channel_update(chan).unwrap())) + } else { + None + } + } + } + } { + return_err!(err, code, &chan_update.encode_with_len()[..]); + } + } + } + + (pending_forward_info, shared_secret, channel_state.unwrap()) + } + /// only fails if the channel does not yet have an assigned short_id fn get_channel_update(&self, chan: &Channel) -> Result { let short_channel_id = match chan.get_short_channel_id() { @@ -1507,183 +1689,34 @@ impl ChannelMessageHandler for ChannelManager { //encrypted with the same key. Its not immediately obvious how to usefully exploit that, //but we should prevent it anyway. - let shared_secret = SharedSecret::new(&self.secp_ctx, &msg.onion_routing_packet.public_key, &self.our_network_key); - let (rho, mu) = ChannelManager::gen_rho_mu_from_shared_secret(&shared_secret); - - macro_rules! get_onion_hash { - () => { - { - let mut sha = Sha256::new(); - sha.input(&msg.onion_routing_packet.hop_data); - let mut onion_hash = [0; 32]; - sha.result(&mut onion_hash); - onion_hash - } - } - } - - macro_rules! return_err { - ($msg: expr, $err_code: expr, $data: expr) => { - return Err(msgs::HandleError { - err: $msg, - action: Some(msgs::ErrorAction::UpdateFailHTLC { - msg: msgs::UpdateFailHTLC { - channel_id: msg.channel_id, - htlc_id: msg.htlc_id, - reason: ChannelManager::build_first_hop_failure_packet(&shared_secret, $err_code, $data), - } - }), - }); - } - } - - if msg.onion_routing_packet.version != 0 { - //TODO: Spec doesn't indicate if we should only hash hop_data here (and in other - //sha256_of_onion error data packets), or the entire onion_routing_packet. Either way, - //the hash doesn't really serve any purpuse - in the case of hashing all data, the - //receiving node would have to brute force to figure out which version was put in the - //packet by the node that send us the message, in the case of hashing the hop_data, the - //node knows the HMAC matched, so they already know what is there... - return_err!("Unknown onion packet version", 0x8000 | 0x4000 | 4, &get_onion_hash!()); - } - - let mut hmac = Hmac::new(Sha256::new(), &mu); - hmac.input(&msg.onion_routing_packet.hop_data); - hmac.input(&msg.payment_hash); - if hmac.result() != MacResult::new(&msg.onion_routing_packet.hmac) { - return_err!("HMAC Check failed", 0x8000 | 0x4000 | 5, &get_onion_hash!()); - } - - let mut chacha = ChaCha20::new(&rho, &[0u8; 8]); - let next_hop_data = { - let mut decoded = [0; 65]; - chacha.process(&msg.onion_routing_packet.hop_data[0..65], &mut decoded); - match msgs::OnionHopData::decode(&decoded[..]) { - Err(err) => { - let error_code = match err { - msgs::DecodeError::UnknownRealmByte => 0x4000 | 1, - _ => 0x2000 | 2, // Should never happen - }; - return_err!("Unable to decode our hop data", error_code, &[0;0]); - }, - Ok(msg) => msg - } - }; - - //TODO: Check that msg.cltv_expiry is within acceptable bounds! - - let mut pending_forward_info = if next_hop_data.hmac == [0; 32] { - // OUR PAYMENT! - if next_hop_data.data.amt_to_forward != msg.amount_msat { - return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat)); - } - if next_hop_data.data.outgoing_cltv_value != msg.cltv_expiry { - return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry)); - } - - // Note that we could obviously respond immediately with an update_fulfill_htlc - // message, however that would leak that we are the recipient of this payment, so - // instead we stay symmetric with the forwarding case, only responding (after a - // delay) once they've send us a commitment_signed! - - PendingForwardHTLCInfo { - onion_packet: None, - payment_hash: msg.payment_hash.clone(), - short_channel_id: 0, - prev_short_channel_id: 0, - amt_to_forward: next_hop_data.data.amt_to_forward, - outgoing_cltv_value: next_hop_data.data.outgoing_cltv_value, - } - } else { - let mut new_packet_data = [0; 20*65]; - chacha.process(&msg.onion_routing_packet.hop_data[65..], &mut new_packet_data[0..19*65]); - chacha.process(&ChannelManager::ZERO[0..65], &mut new_packet_data[19*65..]); - - let mut new_pubkey = msg.onion_routing_packet.public_key.clone(); - - let blinding_factor = { - let mut sha = Sha256::new(); - sha.input(&new_pubkey.serialize()[..]); - sha.input(&shared_secret[..]); - let mut res = [0u8; 32]; - sha.result(&mut res); - match SecretKey::from_slice(&self.secp_ctx, &res) { - Err(_) => { - // Return temporary node failure as its technically our issue, not the - // channel's issue. - return_err!("Blinding factor is an invalid private key", 0x2000 | 2, &[0;0]); - }, - Ok(key) => key - } - }; - - match new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor) { - Err(_) => { - // Return temporary node failure as its technically our issue, not the - // channel's issue. - return_err!("New blinding factor is an invalid private key", 0x2000 | 2, &[0;0]); - }, - Ok(_) => {} - }; - - let outgoing_packet = msgs::OnionPacket { - version: 0, - public_key: new_pubkey, - hop_data: new_packet_data, - hmac: next_hop_data.hmac.clone(), - }; - - PendingForwardHTLCInfo { - onion_packet: Some(outgoing_packet), - payment_hash: msg.payment_hash.clone(), - short_channel_id: next_hop_data.data.short_channel_id, - prev_short_channel_id: 0, - amt_to_forward: next_hop_data.data.amt_to_forward, - outgoing_cltv_value: next_hop_data.data.outgoing_cltv_value, - } - }; - - let mut channel_state_lock = self.channel_state.lock().unwrap(); + let (mut pending_forward_info, shared_secret, mut channel_state_lock) = self.decode_update_add_htlc_onion(msg); let channel_state = channel_state_lock.borrow_parts(); - if pending_forward_info.onion_packet.is_some() { // If short_channel_id is 0 here, we'll reject them in the body here - let forwarding_id = match channel_state.short_to_id.get(&pending_forward_info.short_channel_id) { - None => { - return_err!("Don't have available channel for forwarding as requested.", 0x4000 | 10, &[0;0]); - }, - Some(id) => id.clone(), - }; - let chan = channel_state.by_id.get_mut(&forwarding_id).unwrap(); - let fee = chan.get_our_fee_base_msat(&*self.fee_estimator) + (pending_forward_info.amt_to_forward * self.fee_proportional_millionths as u64 / 1000000) as u32; - if msg.amount_msat < fee as u64 || (msg.amount_msat - fee as u64) < pending_forward_info.amt_to_forward { - log_debug!(self, "HTLC {} incorrect amount: in {} out {} fee required {}", msg.htlc_id, msg.amount_msat, pending_forward_info.amt_to_forward, fee); - let chan_update = self.get_channel_update(chan).unwrap(); - return_err!("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, &chan_update.encode_with_len()[..]); - } - if (msg.cltv_expiry as u64) < pending_forward_info.outgoing_cltv_value as u64 + CLTV_EXPIRY_DELTA as u64 { - log_debug!(self, "HTLC {} incorrect CLTV: in {} out {} delta required {}", msg.htlc_id, msg.cltv_expiry, pending_forward_info.outgoing_cltv_value, CLTV_EXPIRY_DELTA); - let chan_update = self.get_channel_update(chan).unwrap(); - return_err!("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, &chan_update.encode_with_len()[..]); - } - if !chan.is_live() { - let chan_update = self.get_channel_update(chan).unwrap(); - return_err!("Forwarding channel is not in a ready state.", 0x1000 | 7, &chan_update.encode_with_len()[..]); - } - } - let claimable_htlcs_entry = channel_state.claimable_htlcs.entry(msg.payment_hash.clone()); // We dont correctly handle payments that route through us twice on their way to their // destination. That's OK since those nodes are probably busted or trying to do network // mapping through repeated loops. In either case, we want them to stop talking to us, so // we send permanent_node_failure. - if let &hash_map::Entry::Occupied(ref e) = &claimable_htlcs_entry { - let mut acceptable_cycle = false; - if let &PendingOutboundHTLC::OutboundRoute { .. } = e.get() { - acceptable_cycle = pending_forward_info.short_channel_id == 0; - } - if !acceptable_cycle { - return_err!("Payment looped through us twice", 0x4000 | 0x2000 | 2, &[0;0]); + let mut will_forward = false; + if let PendingHTLCStatus::Forward(PendingForwardHTLCInfo { short_channel_id, .. }) = pending_forward_info { + if let &hash_map::Entry::Occupied(ref e) = &claimable_htlcs_entry { + let mut acceptable_cycle = false; + if let &PendingOutboundHTLC::OutboundRoute { .. } = e.get() { + acceptable_cycle = short_channel_id == 0; + } + if !acceptable_cycle { + log_info!(self, "Failed to accept incoming HTLC: Payment looped through us twice"); + pending_forward_info = PendingHTLCStatus::Fail(msgs::UpdateFailHTLC { + channel_id: msg.channel_id, + htlc_id: msg.htlc_id, + reason: ChannelManager::build_first_hop_failure_packet(&shared_secret, 0x4000 | 0x2000 | 2, &[0;0]), + }); + } else { + will_forward = true; + } + } else { + will_forward = true; } } @@ -1696,33 +1729,37 @@ impl ChannelMessageHandler for ChannelManager { return Err(HandleError{err: "Channel not yet available for receiving HTLCs", action: None}); } let short_channel_id = chan.get_short_channel_id().unwrap(); - pending_forward_info.prev_short_channel_id = short_channel_id; + if let PendingHTLCStatus::Forward(ref mut forward_info) = pending_forward_info { + forward_info.prev_short_channel_id = short_channel_id; + } (short_channel_id, chan.update_add_htlc(&msg, pending_forward_info)?) }, None => return Err(HandleError{err: "Failed to find corresponding channel", action: None}), }; - match claimable_htlcs_entry { - hash_map::Entry::Occupied(mut e) => { - let outbound_route = e.get_mut(); - let (route, session_priv) = match outbound_route { - &mut PendingOutboundHTLC::OutboundRoute { ref route, ref session_priv } => { - (route.clone(), session_priv.clone()) - }, - _ => unreachable!(), - }; - *outbound_route = PendingOutboundHTLC::CycledRoute { - source_short_channel_id, - incoming_packet_shared_secret: shared_secret, - route, - session_priv, - }; - }, - hash_map::Entry::Vacant(e) => { - e.insert(PendingOutboundHTLC::IntermediaryHopData { - source_short_channel_id, - incoming_packet_shared_secret: shared_secret, - }); + if will_forward { + match claimable_htlcs_entry { + hash_map::Entry::Occupied(mut e) => { + let outbound_route = e.get_mut(); + let (route, session_priv) = match outbound_route { + &mut PendingOutboundHTLC::OutboundRoute { ref route, ref session_priv } => { + (route.clone(), session_priv.clone()) + }, + _ => unreachable!(), + }; + *outbound_route = PendingOutboundHTLC::CycledRoute { + source_short_channel_id, + incoming_packet_shared_secret: shared_secret, + route, + session_priv, + }; + }, + hash_map::Entry::Vacant(e) => { + e.insert(PendingOutboundHTLC::IntermediaryHopData { + source_short_channel_id, + incoming_packet_shared_secret: shared_secret, + }); + } } }