X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fln%2Frouter.rs;h=d2d428a38bc70a27d3cfec4fc04c09bbc744947f;hb=c91f72c131e8390ec86ace9cc4c70a55cf5f9438;hp=e249913ec579efbe11616e02f57b8e5aff7e0c4e;hpb=07ac327f8be0401235ca85baaef5486b629bfd6a;p=rust-lightning diff --git a/src/ln/router.rs b/src/ln/router.rs index e249913e..d2d428a3 100644 --- a/src/ln/router.rs +++ b/src/ln/router.rs @@ -3,11 +3,14 @@ use secp256k1::{Secp256k1,Message}; use secp256k1; use bitcoin::util::hash::Sha256dHash; +use bitcoin::blockdata::script::Builder; +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; @@ -100,7 +103,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))?; @@ -197,6 +214,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); @@ -207,12 +228,22 @@ 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)) => { - //TODO: Check if script_pubkey matches bitcoin_key_1 and bitcoin_key_2 + let expected_script = Builder::new().push_opcode(opcodes::All::OP_PUSHNUM_2) + .push_slice(&msg.contents.bitcoin_key_1.serialize()) + .push_slice(&msg.contents.bitcoin_key_2.serialize()) + .push_opcode(opcodes::All::OP_PUSHNUM_2).push_opcode(opcodes::All::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); + if script_pubkey != expected_script { + return Err(HandleError{err: "Channel announcement keys didn't match on-chain script", action: Some(ErrorAction::IgnoreError)}); + } + //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) => { - // Tenatively accept, potentially exposing us to DoS attacks + // 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)}); @@ -220,39 +251,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); } }; @@ -292,12 +339,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); } }, } @@ -448,6 +490,25 @@ 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.