X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannelmanager.rs;h=d1395e1271c3d5bc130d53c2cb29e561d4dc7f16;hb=5260e81033c6008fdcef1cbf973243268f4ca373;hp=efcf2159a068fa21b6f18e9674c98960a0f2f501;hpb=d39f25839ad9ff5e2a0e1dd8919e991828d1bf35;p=rust-lightning diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index efcf2159..d1395e12 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -30,7 +30,7 @@ use chain::transaction::OutPoint; use ln::channel::{Channel, ChannelError}; use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, ManyChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY}; use ln::features::{InitFeatures, NodeFeatures}; -use ln::router::Route; +use ln::router::{Route, RouteHop}; use ln::msgs; use ln::onion_utils; use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; @@ -136,7 +136,7 @@ struct ClaimableHTLC { pub(super) enum HTLCSource { PreviousHopData(HTLCPreviousHopData), OutboundRoute { - route: Route, + path: Vec, session_priv: SecretKey, /// Technically we can recalculate this from the route, but we cache it here to avoid /// doing a double-pass on route when we get a failure back @@ -147,7 +147,7 @@ pub(super) enum HTLCSource { impl HTLCSource { pub fn dummy() -> Self { HTLCSource::OutboundRoute { - route: Route { hops: Vec::new() }, + path: Vec::new(), session_priv: SecretKey::from_slice(&[1; 32]).unwrap(), first_hop_htlc_msat: 0, } @@ -1231,13 +1231,16 @@ impl ChannelMan /// bit set (either as required or as available). If multiple paths are present in the Route, /// we assume the invoice had the basic_mpp feature set. pub fn send_payment(&self, route: Route, payment_hash: PaymentHash, payment_secret: &Option) -> Result<(), APIError> { - if route.hops.len() < 1 || route.hops.len() > 20 { - return Err(APIError::RouteError{err: "Route didn't go anywhere/had bogus size"}); + if route.paths.len() < 1 || route.paths.len() > 1 { + return Err(APIError::RouteError{err: "We currently don't support MPP, and we need at least one path"}); + } + if route.paths[0].len() < 1 || route.paths[0].len() > 20 { + return Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"}); } let our_node_id = self.get_our_node_id(); - for (idx, hop) in route.hops.iter().enumerate() { - if idx != route.hops.len() - 1 && hop.pubkey == our_node_id { - return Err(APIError::RouteError{err: "Route went through us but wasn't a simple rebalance loop to us"}); + for (idx, hop) in route.paths[0].iter().enumerate() { + if idx != route.paths[0].len() - 1 && hop.pubkey == our_node_id { + return Err(APIError::RouteError{err: "Path went through us but wasn't a simple rebalance loop to us"}); } } @@ -1245,9 +1248,9 @@ impl ChannelMan let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1; - let onion_keys = secp_call!(onion_utils::construct_onion_keys(&self.secp_ctx, &route, &session_priv), + let onion_keys = secp_call!(onion_utils::construct_onion_keys(&self.secp_ctx, &route.paths[0], &session_priv), APIError::RouteError{err: "Pubkey along hop was maliciously selected"}); - let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route, payment_secret, cur_height)?; + let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], payment_secret, cur_height)?; if onion_utils::route_size_insane(&onion_payloads) { return Err(APIError::RouteError{err: "Route size too large considering onion data"}); } @@ -1257,7 +1260,7 @@ impl ChannelMan let err: Result<(), _> = loop { let mut channel_lock = self.channel_state.lock().unwrap(); - let id = match channel_lock.short_to_id.get(&route.hops.first().unwrap().short_channel_id) { + let id = match channel_lock.short_to_id.get(&route.paths[0].first().unwrap().short_channel_id) { None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"}), Some(id) => id.clone(), }; @@ -1265,14 +1268,14 @@ impl ChannelMan let channel_state = &mut *channel_lock; if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) { match { - if chan.get().get_their_node_id() != route.hops.first().unwrap().pubkey { + if chan.get().get_their_node_id() != route.paths[0].first().unwrap().pubkey { return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"}); } if !chan.get().is_live() { return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!"}); } break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute { - route: route.clone(), + path: route.paths[0].clone(), session_priv: session_priv.clone(), first_hop_htlc_msat: htlc_msat, }, onion_packet), channel_state, chan) @@ -1288,7 +1291,7 @@ impl ChannelMan } channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs { - node_id: route.hops.first().unwrap().pubkey, + node_id: route.paths[0].first().unwrap().pubkey, updates: msgs::CommitmentUpdate { update_add_htlcs: vec![update_add], update_fulfill_htlcs: Vec::new(), @@ -1305,7 +1308,7 @@ impl ChannelMan return Ok(()); }; - match handle_error!(self, err, route.hops.first().unwrap().pubkey) { + match handle_error!(self, err, route.paths[0].first().unwrap().pubkey) { Ok(_) => unreachable!(), Err(e) => { Err(APIError::ChannelUnavailable { err: e.err }) } } @@ -1750,7 +1753,7 @@ impl ChannelMan //between the branches here. We should make this async and move it into the forward HTLCs //timer handling. match source { - HTLCSource::OutboundRoute { ref route, .. } => { + HTLCSource::OutboundRoute { ref path, .. } => { log_trace!(self, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0)); mem::drop(channel_state_lock); match &onion_error { @@ -1792,7 +1795,7 @@ impl ChannelMan self.pending_events.lock().unwrap().push( events::Event::PaymentFailed { payment_hash: payment_hash.clone(), - rejected_by_dest: route.hops.len() == 1, + rejected_by_dest: path.len() == 1, #[cfg(test)] error_code: Some(*failure_code), } @@ -1856,9 +1859,19 @@ impl ChannelMan let mut channel_state = Some(self.channel_state.lock().unwrap()); let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&(payment_hash, *payment_secret)); if let Some(mut sources) = removed_source { + assert!(!sources.is_empty()); + let valid_mpp_amount = if let &Some(ref data) = &sources[0].payment_data { + assert!(payment_secret.is_some()); + data.total_msat == expected_amount + } else { + assert!(payment_secret.is_none()); + false + }; + + let mut claimed_any_htlcs = false; for htlc in sources.drain(..) { if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); } - if htlc.value < expected_amount || htlc.value > expected_amount * 2 { + if !valid_mpp_amount && (htlc.value < expected_amount || htlc.value > expected_amount * 2) { let mut htlc_msat_data = byte_utils::be64_to_array(htlc.value).to_vec(); let mut height_data = byte_utils::be32_to_array(self.latest_block_height.load(Ordering::Acquire) as u32).to_vec(); htlc_msat_data.append(&mut height_data); @@ -1867,9 +1880,10 @@ impl ChannelMan HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_data }); } else { self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), payment_preimage); + claimed_any_htlcs = true; } } - true + claimed_any_htlcs } else { false } } fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard>, source: HTLCSource, payment_preimage: PaymentPreimage) { @@ -3271,9 +3285,9 @@ impl Writeable for HTLCSource { 0u8.write(writer)?; hop_data.write(writer)?; }, - &HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } => { + &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } => { 1u8.write(writer)?; - route.write(writer)?; + path.write(writer)?; session_priv.write(writer)?; first_hop_htlc_msat.write(writer)?; } @@ -3287,7 +3301,7 @@ impl Readable for HTLCSource { match ::read(reader)? { 0 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)), 1 => Ok(HTLCSource::OutboundRoute { - route: Readable::read(reader)?, + path: Readable::read(reader)?, session_priv: Readable::read(reader)?, first_hop_htlc_msat: Readable::read(reader)?, }),