X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fln%2Frouter.rs;h=4a55df88c6f90fd30d309e506377d819a7720a02;hb=d49802fb8f4518e6572d48ce32238eb70ffdb809;hp=e3697147f0a6e0f1d34657d9024ef10b19a4608d;hpb=4b4d8b6b9e3e2d4e1f3a8a78896fb45b426a5523;p=rust-lightning diff --git a/src/ln/router.rs b/src/ln/router.rs index e3697147..4a55df88 100644 --- a/src/ln/router.rs +++ b/src/ln/router.rs @@ -1,3 +1,8 @@ +//! The top-level routing/network map tracking logic lives here. +//! +//! You probably want to create a Router and use that as your RoutingMessageHandler and then +//! interrogate it to get routes for your own payments. + use secp256k1::key::PublicKey; use secp256k1::{Secp256k1,Message}; use secp256k1; @@ -8,8 +13,9 @@ use bitcoin::blockdata::opcodes; use chain::chaininterface::{ChainError, ChainWatchInterface}; use ln::channelmanager; -use ln::msgs::{ErrorAction,HandleError,RoutingMessageHandler,MsgEncodable,NetAddress,GlobalFeatures}; +use ln::msgs::{ErrorAction,HandleError,RoutingMessageHandler,NetAddress,GlobalFeatures}; use ln::msgs; +use util::ser::Writeable; use util::logger::Logger; use std::cmp; @@ -21,6 +27,7 @@ use std; /// A hop in a route #[derive(Clone)] pub struct RouteHop { + /// The node_id of the node at this hop. pub pubkey: PublicKey, /// The channel that should be used from the previous hop to reach this node. pub short_channel_id: u64, @@ -102,7 +109,21 @@ struct NetworkMap { our_node_id: PublicKey, nodes: HashMap, } - +struct MutNetworkMap<'a> { + #[cfg(feature = "non_bitcoin_chain_hash_routing")] + channels: &'a mut HashMap<(u64, Sha256dHash), ChannelInfo>, + #[cfg(not(feature = "non_bitcoin_chain_hash_routing"))] + channels: &'a mut HashMap, + nodes: &'a mut HashMap, +} +impl NetworkMap { + fn borrow_parts(&mut self) -> MutNetworkMap { + MutNetworkMap { + channels: &mut self.channels, + nodes: &mut self.nodes, + } + } +} impl std::fmt::Display for NetworkMap { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { write!(f, "Node id {} network map\n[Channels]\n", log_pubkey!(self.our_node_id))?; @@ -145,11 +166,18 @@ impl NetworkMap { /// A channel descriptor which provides a last-hop route to get_route pub struct RouteHint { + /// The node_id of the non-target end of the route pub src_node_id: PublicKey, + /// The short_channel_id of this channel pub short_channel_id: u64, + /// The static msat-denominated fee which must be paid to use this channel pub fee_base_msat: u32, + /// The dynamic proportional fee which must be paid to use this channel, denominated in + /// millionths of the value being forwarded to the next hop. pub fee_proportional_millionths: u32, + /// The difference in CLTV values between this node and the next node. pub cltv_expiry_delta: u16, + /// The minimum value, in msat, which must be relayed to the next hop. pub htlc_minimum_msat: u64, } @@ -199,6 +227,10 @@ impl RoutingMessageHandler for Router { } fn handle_channel_announcement(&self, msg: &msgs::ChannelAnnouncement) -> Result { + if msg.contents.node_id_1 == msg.contents.node_id_2 || msg.contents.bitcoin_key_1 == msg.contents.bitcoin_key_2 { + return Err(HandleError{err: "Channel announcement node had a channel with itself", action: Some(ErrorAction::IgnoreError)}); + } + let msg_hash = Message::from_slice(&Sha256dHash::from_data(&msg.contents.encode()[..])[..]).unwrap(); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_1, &msg.contents.node_id_1); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_2, &msg.contents.node_id_2); @@ -209,7 +241,7 @@ impl RoutingMessageHandler for Router { panic!("Unknown-required-features ChannelAnnouncements should never deserialize!"); } - match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) { + let checked_utxo = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) { Ok((script_pubkey, _value)) => { let expected_script = Builder::new().push_opcode(opcodes::All::OP_PUSHNUM_2) .push_slice(&msg.contents.bitcoin_key_1.serialize()) @@ -220,9 +252,11 @@ impl RoutingMessageHandler for Router { } //TODO: Check if value is worth storing, use it to inform routing, and compare it //to the new HTLC max field in channel_update + true }, Err(ChainError::NotSupported) => { // Tentatively accept, potentially exposing us to DoS attacks + false }, Err(ChainError::NotWatched) => { return Err(HandleError{err: "Channel announced on an unknown chain", action: Some(ErrorAction::IgnoreError)}); @@ -230,39 +264,55 @@ impl RoutingMessageHandler for Router { Err(ChainError::UnknownTx) => { return Err(HandleError{err: "Channel announced without corresponding UTXO entry", action: Some(ErrorAction::IgnoreError)}); }, - } + }; - let mut network = self.network_map.write().unwrap(); + let mut network_lock = self.network_map.write().unwrap(); + let network = network_lock.borrow_parts(); + + let chan_info = ChannelInfo { + features: msg.contents.features.clone(), + one_to_two: DirectionalChannelInfo { + src_node_id: msg.contents.node_id_1.clone(), + last_update: 0, + enabled: false, + cltv_expiry_delta: u16::max_value(), + htlc_minimum_msat: u64::max_value(), + fee_base_msat: u32::max_value(), + fee_proportional_millionths: u32::max_value(), + }, + two_to_one: DirectionalChannelInfo { + src_node_id: msg.contents.node_id_2.clone(), + last_update: 0, + enabled: false, + cltv_expiry_delta: u16::max_value(), + htlc_minimum_msat: u64::max_value(), + fee_base_msat: u32::max_value(), + fee_proportional_millionths: u32::max_value(), + } + }; match network.channels.entry(NetworkMap::get_key(msg.contents.short_channel_id, msg.contents.chain_hash)) { - Entry::Occupied(_) => { + Entry::Occupied(mut entry) => { //TODO: because asking the blockchain if short_channel_id is valid is only optional //in the blockchain API, we need to handle it smartly here, though its unclear //exactly how... - return Err(HandleError{err: "Already have knowledge of channel", action: Some(ErrorAction::IgnoreError)}) + if checked_utxo { + // Either our UTXO provider is busted, there was a reorg, or the UTXO provider + // only sometimes returns results. In any case remove the previous entry. Note + // that the spec expects us to "blacklist" the node_ids involved, but we can't + // do that because + // a) we don't *require* a UTXO provider that always returns results. + // b) we don't track UTXOs of channels we know about and remove them if they + // get reorg'd out. + // c) it's unclear how to do so without exposing ourselves to massive DoS risk. + Self::remove_channel_in_nodes(network.nodes, &entry.get(), msg.contents.short_channel_id); + *entry.get_mut() = chan_info; + } else { + return Err(HandleError{err: "Already have knowledge of channel", action: Some(ErrorAction::IgnoreError)}) + } }, Entry::Vacant(entry) => { - entry.insert(ChannelInfo { - features: msg.contents.features.clone(), - one_to_two: DirectionalChannelInfo { - src_node_id: msg.contents.node_id_1.clone(), - last_update: 0, - enabled: false, - cltv_expiry_delta: u16::max_value(), - htlc_minimum_msat: u64::max_value(), - fee_base_msat: u32::max_value(), - fee_proportional_millionths: u32::max_value(), - }, - two_to_one: DirectionalChannelInfo { - src_node_id: msg.contents.node_id_2.clone(), - last_update: 0, - enabled: false, - cltv_expiry_delta: u16::max_value(), - htlc_minimum_msat: u64::max_value(), - fee_base_msat: u32::max_value(), - fee_proportional_millionths: u32::max_value(), - } - }); + entry.insert(chan_info); } }; @@ -302,12 +352,7 @@ impl RoutingMessageHandler for Router { &msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => { let mut network = self.network_map.write().unwrap(); if let Some(chan) = network.channels.remove(short_channel_id) { - network.nodes.get_mut(&chan.one_to_two.src_node_id).unwrap().channels.retain(|chan_id| { - chan_id != NetworkMap::get_short_id(chan_id) - }); - network.nodes.get_mut(&chan.two_to_one.src_node_id).unwrap().channels.retain(|chan_id| { - chan_id != NetworkMap::get_short_id(chan_id) - }); + Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id); } }, } @@ -412,6 +457,7 @@ struct DummyDirectionalChannelInfo { } impl Router { + /// Creates a new router with the given node_id to be used as the source for get_route() pub fn new(our_pubkey: PublicKey, chain_monitor: Arc, logger: Arc) -> Router { let mut nodes = HashMap::new(); nodes.insert(our_pubkey.clone(), NodeInfo { @@ -458,14 +504,38 @@ impl Router { unimplemented!(); } + fn remove_channel_in_nodes(nodes: &mut HashMap, chan: &ChannelInfo, short_channel_id: u64) { + macro_rules! remove_from_node { + ($node_id: expr) => { + if let Entry::Occupied(mut entry) = nodes.entry($node_id) { + entry.get_mut().channels.retain(|chan_id| { + short_channel_id != *NetworkMap::get_short_id(chan_id) + }); + if entry.get().channels.is_empty() { + entry.remove_entry(); + } + } else { + panic!("Had channel that pointed to unknown node (ie inconsistent network map)!"); + } + } + } + remove_from_node!(chan.one_to_two.src_node_id); + remove_from_node!(chan.two_to_one.src_node_id); + } + /// Gets a route from us to the given target node. + /// /// Extra routing hops between known nodes and the target will be used if they are included in /// last_hops. + /// /// If some channels aren't announced, it may be useful to fill in a first_hops with the /// results from a local ChannelManager::list_usable_channels() call. If it is filled in, our /// (this Router's) view of our local channels will be ignored, and only those in first_hops - /// will be used. Panics if first_hops contains channels without short_channel_ids + /// will be used. + /// + /// Panics if first_hops contains channels without short_channel_ids /// (ChannelManager::list_usable_channels will never include such channels). + /// /// The fees on channels from us to next-hops are ignored (as they are assumed to all be /// equal), however the enabled/disabled bit on such channels as well as the htlc_minimum_msat /// *is* checked as they may change based on the receiving node.