X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Frouter.rs;h=2b23ae56ddf9a1514f10f7edbd7571500982a449;hb=e94635703a5a9f51a5f42849ba0ccfb6e5e98ac3;hp=5686e5a3bf7b333f4458d384b3a4ad5b7f2cd3ca;hpb=126b514168ff8294f6ee7b9573797c6759512b9c;p=rust-lightning diff --git a/lightning/src/ln/router.rs b/lightning/src/ln/router.rs index 5686e5a3..2b23ae56 100644 --- a/lightning/src/ln/router.rs +++ b/lightning/src/ln/router.rs @@ -14,13 +14,15 @@ use bitcoin::blockdata::opcodes; use chain::chaininterface::{ChainError, ChainWatchInterface}; use ln::channelmanager; -use ln::msgs::{DecodeError,ErrorAction,LightningError,RoutingMessageHandler,NetAddress,GlobalFeatures}; +use ln::features::{ChannelFeatures, NodeFeatures}; +use ln::msgs::{DecodeError,ErrorAction,LightningError,RoutingMessageHandler,NetAddress}; use ln::msgs; use util::ser::{Writeable, Readable, Writer, ReadableArgs}; use util::logger::Logger; use std::cmp; use std::sync::{RwLock,Arc}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::collections::{HashMap,BinaryHeap,BTreeMap}; use std::collections::btree_map::Entry as BtreeEntry; use std; @@ -30,8 +32,14 @@ use std; pub struct RouteHop { /// The node_id of the node at this hop. pub pubkey: PublicKey, + /// The node_announcement features of the node at this hop. For the last hop, these may be + /// amended to match the features present in the invoice this node generated. + pub node_features: NodeFeatures, /// The channel that should be used from the previous hop to reach this node. pub short_channel_id: u64, + /// The channel_announcement features of the channel that should be used from the previous hop + /// to reach this node. + pub channel_features: ChannelFeatures, /// The fee taken on this hop. For the last hop, this should be the full value of the payment. pub fee_msat: u64, /// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value @@ -53,7 +61,9 @@ impl Writeable for Route { (self.hops.len() as u8).write(writer)?; for hop in self.hops.iter() { hop.pubkey.write(writer)?; + hop.node_features.write(writer)?; hop.short_channel_id.write(writer)?; + hop.channel_features.write(writer)?; hop.fee_msat.write(writer)?; hop.cltv_expiry_delta.write(writer)?; } @@ -61,14 +71,16 @@ impl Writeable for Route { } } -impl Readable for Route { - fn read(reader: &mut R) -> Result { +impl Readable for Route { + fn read(reader: &mut R) -> Result { let hops_count: u8 = Readable::read(reader)?; let mut hops = Vec::with_capacity(hops_count as usize); for _ in 0..hops_count { hops.push(RouteHop { pubkey: Readable::read(reader)?, + node_features: Readable::read(reader)?, short_channel_id: Readable::read(reader)?, + channel_features: Readable::read(reader)?, fee_msat: Readable::read(reader)?, cltv_expiry_delta: Readable::read(reader)?, }); @@ -111,7 +123,7 @@ impl_writeable!(DirectionalChannelInfo, 0, { #[derive(PartialEq)] struct ChannelInfo { - features: GlobalFeatures, + features: ChannelFeatures, one_to_two: DirectionalChannelInfo, two_to_one: DirectionalChannelInfo, //this is cached here so we can send out it later if required by route_init_sync @@ -143,7 +155,7 @@ struct NodeInfo { lowest_inbound_channel_fee_base_msat: u32, lowest_inbound_channel_fee_proportional_millionths: u32, - features: GlobalFeatures, + features: NodeFeatures, last_update: u32, rgb: [u8; 3], alias: [u8; 32], @@ -183,8 +195,8 @@ impl Writeable for NodeInfo { const MAX_ALLOC_SIZE: u64 = 64*1024; -impl Readable for NodeInfo { - fn read(reader: &mut R) -> Result { +impl Readable for NodeInfo { + fn read(reader: &mut R) -> Result { let channels_count: u64 = Readable::read(reader)?; let mut channels = Vec::with_capacity(cmp::min(channels_count, MAX_ALLOC_SIZE / 8) as usize); for _ in 0..channels_count { @@ -249,8 +261,8 @@ impl Writeable for NetworkMap { } } -impl Readable for NetworkMap { - fn read(reader: &mut R) -> Result { +impl Readable for NetworkMap { + fn read(reader: &mut R) -> Result { let channels_count: u64 = Readable::read(reader)?; let mut channels = BTreeMap::new(); for _ in 0..channels_count { @@ -274,21 +286,6 @@ impl Readable for NetworkMap { } } -struct MutNetworkMap<'a> { - #[cfg(feature = "non_bitcoin_chain_hash_routing")] - channels: &'a mut BTreeMap<(u64, Sha256dHash), ChannelInfo>, - #[cfg(not(feature = "non_bitcoin_chain_hash_routing"))] - channels: &'a mut BTreeMap, - nodes: &'a mut BTreeMap, -} -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))?; @@ -351,6 +348,7 @@ pub struct RouteHint { pub struct Router { secp_ctx: Secp256k1, network_map: RwLock, + full_syncs_requested: AtomicUsize, chain_monitor: Arc, logger: Arc, } @@ -383,8 +381,8 @@ pub struct RouterReadArgs { pub logger: Arc, } -impl ReadableArgs for Router { - fn read(reader: &mut R, args: RouterReadArgs) -> Result { +impl ReadableArgs for Router { + fn read(reader: &mut R, args: RouterReadArgs) -> Result { let _ver: u8 = Readable::read(reader)?; let min_ver: u8 = Readable::read(reader)?; if min_ver > SERIALIZATION_VERSION { @@ -394,6 +392,7 @@ impl ReadableArgs for Router { Ok(Router { secp_ctx: Secp256k1::verification_only(), network_map: RwLock::new(network_map), + full_syncs_requested: AtomicUsize::new(0), chain_monitor: args.chain_monitor, logger: args.logger, }) @@ -410,14 +409,11 @@ macro_rules! secp_verify_sig { } impl RoutingMessageHandler for Router { + fn handle_node_announcement(&self, msg: &msgs::NodeAnnouncement) -> Result { let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.signature, &msg.contents.node_id); - if msg.contents.features.requires_unknown_bits() { - panic!("Unknown-required-features NodeAnnouncements should never deserialize!"); - } - let mut network = self.network_map.write().unwrap(); match network.nodes.get_mut(&msg.contents.node_id) { None => Err(LightningError{err: "No existing channels for node_announcement", action: ErrorAction::IgnoreError}), @@ -432,7 +428,7 @@ impl RoutingMessageHandler for Router { node.alias = msg.contents.alias; node.addresses = msg.contents.addresses.clone(); - let should_relay = msg.contents.excess_data.is_empty() && msg.contents.excess_address_data.is_empty() && !msg.contents.features.supports_unknown_bits(); + let should_relay = msg.contents.excess_data.is_empty() && msg.contents.excess_address_data.is_empty(); node.announcement_message = if should_relay { Some(msg.clone()) } else { None }; Ok(should_relay) } @@ -450,10 +446,6 @@ impl RoutingMessageHandler for Router { secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_1, &msg.contents.bitcoin_key_1); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_2, &msg.contents.bitcoin_key_2); - if msg.contents.features.requires_unknown_bits() { - panic!("Unknown-required-features ChannelAnnouncements should never deserialize!"); - } - 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) @@ -481,9 +473,9 @@ impl RoutingMessageHandler for Router { }; let mut network_lock = self.network_map.write().unwrap(); - let network = network_lock.borrow_parts(); + let network = &mut *network_lock; - let should_relay = msg.contents.excess_data.is_empty() && !msg.contents.features.supports_unknown_bits(); + let should_relay = msg.contents.excess_data.is_empty(); let chan_info = ChannelInfo { features: msg.contents.features.clone(), @@ -524,7 +516,7 @@ impl RoutingMessageHandler for Router { // 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); + Self::remove_channel_in_nodes(&mut network.nodes, &entry.get(), msg.contents.short_channel_id); *entry.get_mut() = chan_info; } else { return Err(LightningError{err: "Already have knowledge of channel", action: ErrorAction::IgnoreError}) @@ -546,7 +538,7 @@ impl RoutingMessageHandler for Router { channels: vec!(NetworkMap::get_key(msg.contents.short_channel_id, msg.contents.chain_hash)), lowest_inbound_channel_fee_base_msat: u32::max_value(), lowest_inbound_channel_fee_proportional_millionths: u32::max_value(), - features: GlobalFeatures::new(), + features: NodeFeatures::empty(), last_update: 0, rgb: [0; 3], alias: [0; 32], @@ -710,6 +702,17 @@ impl RoutingMessageHandler for Router { } result } + + fn should_request_full_sync(&self, _node_id: &PublicKey) -> bool { + //TODO: Determine whether to request a full sync based on the network map. + const FULL_SYNCS_TO_REQUEST: usize = 5; + if self.full_syncs_requested.load(Ordering::Acquire) < FULL_SYNCS_TO_REQUEST { + self.full_syncs_requested.fetch_add(1, Ordering::AcqRel); + true + } else { + false + } + } } #[derive(Eq, PartialEq)] @@ -748,7 +751,7 @@ impl Router { channels: Vec::new(), lowest_inbound_channel_fee_base_msat: u32::max_value(), lowest_inbound_channel_fee_proportional_millionths: u32::max_value(), - features: GlobalFeatures::new(), + features: NodeFeatures::empty(), last_update: 0, rgb: [0; 3], alias: [0; 32], @@ -762,6 +765,7 @@ impl Router { our_node_id: our_pubkey, nodes: nodes, }), + full_syncs_requested: AtomicUsize::new(0), chain_monitor, logger, } @@ -862,13 +866,15 @@ impl Router { return Ok(Route { hops: vec![RouteHop { pubkey: chan.remote_network_id, + node_features: NodeFeatures::with_known_relevant_init_flags(&chan.counterparty_features), short_channel_id, + channel_features: ChannelFeatures::with_known_relevant_init_flags(&chan.counterparty_features), fee_msat: final_value_msat, cltv_expiry_delta: final_cltv, }], }); } - first_hop_targets.insert(chan.remote_network_id, short_channel_id); + first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone())); } if first_hop_targets.is_empty() { return Err(LightningError{err: "Cannot route when there are no outbound routes away from us", action: ErrorAction::IgnoreError}); @@ -879,7 +885,7 @@ impl Router { // Adds entry which goes from the node pointed to by $directional_info to // $dest_node_id over the channel with id $chan_id with fees described in // $directional_info. - ( $chan_id: expr, $dest_node_id: expr, $directional_info: expr, $starting_fee_msat: expr ) => { + ( $chan_id: expr, $dest_node_id: expr, $directional_info: expr, $chan_features: expr, $starting_fee_msat: expr ) => { //TODO: Explore simply adding fee to hit htlc_minimum_msat if $starting_fee_msat as u64 + final_value_msat >= $directional_info.htlc_minimum_msat { let proportional_fee_millions = ($starting_fee_msat + final_value_msat).checked_mul($directional_info.fee_proportional_millionths as u64); @@ -895,7 +901,9 @@ impl Router { node.lowest_inbound_channel_fee_proportional_millionths, RouteHop { pubkey: $dest_node_id.clone(), + node_features: NodeFeatures::empty(), short_channel_id: 0, + channel_features: $chan_features.clone(), fee_msat: 0, cltv_expiry_delta: 0, }) @@ -921,7 +929,9 @@ impl Router { old_entry.0 = total_fee; old_entry.3 = RouteHop { pubkey: $dest_node_id.clone(), + node_features: NodeFeatures::empty(), short_channel_id: $chan_id.clone(), + channel_features: $chan_features.clone(), fee_msat: new_fee, // This field is ignored on the last-hop anyway cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32, } @@ -934,24 +944,28 @@ impl Router { macro_rules! add_entries_to_cheapest_to_target_node { ( $node: expr, $node_id: expr, $fee_to_target_msat: expr ) => { if first_hops.is_some() { - if let Some(first_hop) = first_hop_targets.get(&$node_id) { - add_entry!(first_hop, $node_id, dummy_directional_info, $fee_to_target_msat); + if let Some(&(ref first_hop, ref features)) = first_hop_targets.get(&$node_id) { + add_entry!(first_hop, $node_id, dummy_directional_info, ChannelFeatures::with_known_relevant_init_flags(&features), $fee_to_target_msat); } } - for chan_id in $node.channels.iter() { - let chan = network.channels.get(chan_id).unwrap(); - if chan.one_to_two.src_node_id == *$node_id { - // ie $node is one, ie next hop in A* is two, via the two_to_one channel - if first_hops.is_none() || chan.two_to_one.src_node_id != network.our_node_id { - if chan.two_to_one.enabled { - add_entry!(chan_id, chan.one_to_two.src_node_id, chan.two_to_one, $fee_to_target_msat); - } - } - } else { - if first_hops.is_none() || chan.one_to_two.src_node_id != network.our_node_id { - if chan.one_to_two.enabled { - add_entry!(chan_id, chan.two_to_one.src_node_id, chan.one_to_two, $fee_to_target_msat); + if !$node.features.requires_unknown_bits() { + for chan_id in $node.channels.iter() { + let chan = network.channels.get(chan_id).unwrap(); + if !chan.features.requires_unknown_bits() { + if chan.one_to_two.src_node_id == *$node_id { + // ie $node is one, ie next hop in A* is two, via the two_to_one channel + if first_hops.is_none() || chan.two_to_one.src_node_id != network.our_node_id { + if chan.two_to_one.enabled { + add_entry!(chan_id, chan.one_to_two.src_node_id, chan.two_to_one, chan.features, $fee_to_target_msat); + } + } + } else { + if first_hops.is_none() || chan.one_to_two.src_node_id != network.our_node_id { + if chan.one_to_two.enabled { + add_entry!(chan_id, chan.two_to_one.src_node_id, chan.one_to_two, chan.features, $fee_to_target_msat); + } + } } } } @@ -970,11 +984,17 @@ impl Router { if first_hops.is_none() || hop.src_node_id != network.our_node_id { // first_hop overrules last_hops if network.nodes.get(&hop.src_node_id).is_some() { if first_hops.is_some() { - if let Some(first_hop) = first_hop_targets.get(&hop.src_node_id) { - add_entry!(first_hop, hop.src_node_id, dummy_directional_info, 0); + if let Some(&(ref first_hop, ref features)) = first_hop_targets.get(&hop.src_node_id) { + // Currently there are no channel-context features defined, so we are a + // bit lazy here. In the future, we should pull them out via our + // ChannelManager, but there's no reason to waste the space until we + // need them. + add_entry!(first_hop, hop.src_node_id, dummy_directional_info, ChannelFeatures::with_known_relevant_init_flags(&features), 0); } } - add_entry!(hop.short_channel_id, target, hop, 0); + // BOLT 11 doesn't allow inclusion of features for the last hop hints, which + // really sucks, cause we're gonna need that eventually. + add_entry!(hop.short_channel_id, target, hop, ChannelFeatures::empty(), 0); } } } @@ -982,7 +1002,22 @@ impl Router { while let Some(RouteGraphNode { pubkey, lowest_fee_to_node, .. }) = targets.pop() { if pubkey == network.our_node_id { let mut res = vec!(dist.remove(&network.our_node_id).unwrap().3); - while res.last().unwrap().pubkey != *target { + loop { + if let Some(&(_, ref features)) = first_hop_targets.get(&res.last().unwrap().pubkey) { + res.last_mut().unwrap().node_features = NodeFeatures::with_known_relevant_init_flags(&features); + } else if let Some(node) = network.nodes.get(&res.last().unwrap().pubkey) { + res.last_mut().unwrap().node_features = node.features.clone(); + } else { + // We should be able to fill in features for everything except the last + // hop, if the last hop was provided via a BOLT 11 invoice (though we + // should be able to extend it further as BOLT 11 does have feature + // flags for the last hop node itself). + assert!(res.last().unwrap().pubkey == *target); + } + if res.last().unwrap().pubkey == *target { + break; + } + let new_entry = match dist.remove(&res.last().unwrap().pubkey) { Some(hop) => hop.3, None => return Err(LightningError{err: "Failed to find a non-fee-overflowing path to the given destination", action: ErrorAction::IgnoreError}), @@ -1015,7 +1050,8 @@ mod tests { use chain::chaininterface; use ln::channelmanager; use ln::router::{Router,NodeInfo,NetworkMap,ChannelInfo,DirectionalChannelInfo,RouteHint}; - use ln::msgs::GlobalFeatures; + use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; + use ln::msgs::{ErrorAction, LightningError, RoutingMessageHandler}; use util::test_utils; use util::test_utils::TestVecWriter; use util::logger::Logger; @@ -1028,17 +1064,23 @@ mod tests { use hex; use secp256k1::key::{PublicKey,SecretKey}; + use secp256k1::All; use secp256k1::Secp256k1; use std::sync::Arc; - #[test] - fn route_test() { + fn create_router() -> (Secp256k1, PublicKey, Router) { let secp_ctx = Secp256k1::new(); let our_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap()); let logger: Arc = Arc::new(test_utils::TestLogger::new()); let chain_monitor = Arc::new(chaininterface::ChainWatchInterfaceUtil::new(Network::Testnet, Arc::clone(&logger))); let router = Router::new(our_id, chain_monitor, Arc::clone(&logger)); + (secp_ctx, our_id, router) + } + + #[test] + fn route_test() { + let (secp_ctx, our_id, router) = create_router(); // Build network from our_id to node8: // @@ -1108,6 +1150,23 @@ mod tests { let zero_hash = Sha256dHash::hash(&[0; 32]); + macro_rules! id_to_feature_flags { + // Set the feature flags to the id'th odd (ie non-required) feature bit so that we can + // test for it later. + ($id: expr) => { { + let idx = ($id - 1) * 2 + 1; + if idx > 8*3 { + vec![1 << (idx - 8*3), 0, 0, 0] + } else if idx > 8*2 { + vec![1 << (idx - 8*2), 0, 0] + } else if idx > 8*1 { + vec![1 << (idx - 8*1), 0] + } else { + vec![1 << idx] + } + } } + } + { let mut network = router.network_map.write().unwrap(); @@ -1115,7 +1174,7 @@ mod tests { channels: vec!(NetworkMap::get_key(1, zero_hash.clone()), NetworkMap::get_key(3, zero_hash.clone())), lowest_inbound_channel_fee_base_msat: 100, lowest_inbound_channel_fee_proportional_millionths: 0, - features: GlobalFeatures::new(), + features: NodeFeatures::from_le_bytes(id_to_feature_flags!(1)), last_update: 1, rgb: [0; 3], alias: [0; 32], @@ -1123,7 +1182,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(1, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(1)), one_to_two: DirectionalChannelInfo { src_node_id: our_id.clone(), last_update: 0, @@ -1149,7 +1208,7 @@ mod tests { channels: vec!(NetworkMap::get_key(2, zero_hash.clone()), NetworkMap::get_key(4, zero_hash.clone())), lowest_inbound_channel_fee_base_msat: 0, lowest_inbound_channel_fee_proportional_millionths: 0, - features: GlobalFeatures::new(), + features: NodeFeatures::from_le_bytes(id_to_feature_flags!(2)), last_update: 1, rgb: [0; 3], alias: [0; 32], @@ -1157,7 +1216,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(2, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(2)), one_to_two: DirectionalChannelInfo { src_node_id: our_id.clone(), last_update: 0, @@ -1183,7 +1242,7 @@ mod tests { channels: vec!(NetworkMap::get_key(12, zero_hash.clone()), NetworkMap::get_key(13, zero_hash.clone())), lowest_inbound_channel_fee_base_msat: 0, lowest_inbound_channel_fee_proportional_millionths: 0, - features: GlobalFeatures::new(), + features: NodeFeatures::from_le_bytes(id_to_feature_flags!(8)), last_update: 1, rgb: [0; 3], alias: [0; 32], @@ -1191,7 +1250,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(12, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(12)), one_to_two: DirectionalChannelInfo { src_node_id: our_id.clone(), last_update: 0, @@ -1223,7 +1282,7 @@ mod tests { NetworkMap::get_key(7, zero_hash.clone())), lowest_inbound_channel_fee_base_msat: 0, lowest_inbound_channel_fee_proportional_millionths: 0, - features: GlobalFeatures::new(), + features: NodeFeatures::from_le_bytes(id_to_feature_flags!(3)), last_update: 1, rgb: [0; 3], alias: [0; 32], @@ -1231,7 +1290,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(3, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(3)), one_to_two: DirectionalChannelInfo { src_node_id: node1.clone(), last_update: 0, @@ -1254,7 +1313,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(4, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(4)), one_to_two: DirectionalChannelInfo { src_node_id: node2.clone(), last_update: 0, @@ -1277,7 +1336,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(13, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(13)), one_to_two: DirectionalChannelInfo { src_node_id: node8.clone(), last_update: 0, @@ -1303,7 +1362,7 @@ mod tests { channels: vec!(NetworkMap::get_key(5, zero_hash.clone()), NetworkMap::get_key(11, zero_hash.clone())), lowest_inbound_channel_fee_base_msat: 0, lowest_inbound_channel_fee_proportional_millionths: 0, - features: GlobalFeatures::new(), + features: NodeFeatures::from_le_bytes(id_to_feature_flags!(4)), last_update: 1, rgb: [0; 3], alias: [0; 32], @@ -1311,7 +1370,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(5, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(5)), one_to_two: DirectionalChannelInfo { src_node_id: node3.clone(), last_update: 0, @@ -1337,7 +1396,7 @@ mod tests { channels: vec!(NetworkMap::get_key(6, zero_hash.clone()), NetworkMap::get_key(11, zero_hash.clone())), lowest_inbound_channel_fee_base_msat: 0, lowest_inbound_channel_fee_proportional_millionths: 0, - features: GlobalFeatures::new(), + features: NodeFeatures::from_le_bytes(id_to_feature_flags!(5)), last_update: 1, rgb: [0; 3], alias: [0; 32], @@ -1345,7 +1404,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(6, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(6)), one_to_two: DirectionalChannelInfo { src_node_id: node3.clone(), last_update: 0, @@ -1368,7 +1427,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(11, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(11)), one_to_two: DirectionalChannelInfo { src_node_id: node5.clone(), last_update: 0, @@ -1394,7 +1453,7 @@ mod tests { channels: vec!(NetworkMap::get_key(7, zero_hash.clone())), lowest_inbound_channel_fee_base_msat: 0, lowest_inbound_channel_fee_proportional_millionths: 0, - features: GlobalFeatures::new(), + features: NodeFeatures::from_le_bytes(id_to_feature_flags!(6)), last_update: 1, rgb: [0; 3], alias: [0; 32], @@ -1402,7 +1461,7 @@ mod tests { announcement_message: None, }); network.channels.insert(NetworkMap::get_key(7, zero_hash.clone()), ChannelInfo { - features: GlobalFeatures::new(), + features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(7)), one_to_two: DirectionalChannelInfo { src_node_id: node3.clone(), last_update: 0, @@ -1434,13 +1493,119 @@ mod tests { assert_eq!(route.hops[0].short_channel_id, 2); assert_eq!(route.hops[0].fee_msat, 100); assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2)); + assert_eq!(route.hops[0].channel_features.le_flags(), &id_to_feature_flags!(2)); assert_eq!(route.hops[1].pubkey, node3); assert_eq!(route.hops[1].short_channel_id, 4); assert_eq!(route.hops[1].fee_msat, 100); assert_eq!(route.hops[1].cltv_expiry_delta, 42); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4)); + } + + { // Disable channels 4 and 12 by requiring unknown feature bits + let mut network = router.network_map.write().unwrap(); + network.channels.get_mut(&NetworkMap::get_key(4, zero_hash.clone())).unwrap().features.set_require_unknown_bits(); + network.channels.get_mut(&NetworkMap::get_key(12, zero_hash.clone())).unwrap().features.set_require_unknown_bits(); + } + + { // If all the channels require some features we don't understand, route should fail + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = router.get_route(&node3, None, &Vec::new(), 100, 42) { + assert_eq!(err, "Failed to find a path to the given destination"); + } else { panic!(); } + } + + { // If we specify a channel to node8, that overrides our local channel view and that gets used + let our_chans = vec![channelmanager::ChannelDetails { + channel_id: [0; 32], + short_channel_id: Some(42), + remote_network_id: node8.clone(), + counterparty_features: InitFeatures::from_le_bytes(vec![0b11]), + channel_value_satoshis: 0, + user_id: 0, + outbound_capacity_msat: 0, + inbound_capacity_msat: 0, + is_live: true, + }]; + let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap(); + assert_eq!(route.hops.len(), 2); + + assert_eq!(route.hops[0].pubkey, node8); + assert_eq!(route.hops[0].short_channel_id, 42); + assert_eq!(route.hops[0].fee_msat, 200); + assert_eq!(route.hops[0].cltv_expiry_delta, (13 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features + assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion + + assert_eq!(route.hops[1].pubkey, node3); + assert_eq!(route.hops[1].short_channel_id, 13); + assert_eq!(route.hops[1].fee_msat, 100); + assert_eq!(route.hops[1].cltv_expiry_delta, 42); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(13)); + } + + { // Re-enable channels 4 and 12 by wiping the unknown feature bits + let mut network = router.network_map.write().unwrap(); + network.channels.get_mut(&NetworkMap::get_key(4, zero_hash.clone())).unwrap().features.clear_require_unknown_bits(); + network.channels.get_mut(&NetworkMap::get_key(12, zero_hash.clone())).unwrap().features.clear_require_unknown_bits(); + } + + { // Disable nodes 1, 2, and 8 by requiring unknown feature bits + let mut network = router.network_map.write().unwrap(); + network.nodes.get_mut(&node1).unwrap().features.set_require_unknown_bits(); + network.nodes.get_mut(&node2).unwrap().features.set_require_unknown_bits(); + network.nodes.get_mut(&node8).unwrap().features.set_require_unknown_bits(); + } + + { // If all nodes require some features we don't understand, route should fail + if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = router.get_route(&node3, None, &Vec::new(), 100, 42) { + assert_eq!(err, "Failed to find a path to the given destination"); + } else { panic!(); } + } + + { // If we specify a channel to node8, that overrides our local channel view and that gets used + let our_chans = vec![channelmanager::ChannelDetails { + channel_id: [0; 32], + short_channel_id: Some(42), + remote_network_id: node8.clone(), + counterparty_features: InitFeatures::from_le_bytes(vec![0b11]), + channel_value_satoshis: 0, + user_id: 0, + outbound_capacity_msat: 0, + inbound_capacity_msat: 0, + is_live: true, + }]; + let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap(); + assert_eq!(route.hops.len(), 2); + + assert_eq!(route.hops[0].pubkey, node8); + assert_eq!(route.hops[0].short_channel_id, 42); + assert_eq!(route.hops[0].fee_msat, 200); + assert_eq!(route.hops[0].cltv_expiry_delta, (13 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]); // it should also override our view of their features + assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion + + assert_eq!(route.hops[1].pubkey, node3); + assert_eq!(route.hops[1].short_channel_id, 13); + assert_eq!(route.hops[1].fee_msat, 100); + assert_eq!(route.hops[1].cltv_expiry_delta, 42); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(13)); } + { // Re-enable nodes 1, 2, and 8 + let mut network = router.network_map.write().unwrap(); + network.nodes.get_mut(&node1).unwrap().features.clear_require_unknown_bits(); + network.nodes.get_mut(&node2).unwrap().features.clear_require_unknown_bits(); + network.nodes.get_mut(&node8).unwrap().features.clear_require_unknown_bits(); + } + + // Note that we don't test disabling node 3 and failing to route to it, as we (somewhat + // naively) assume that the user checked the feature bits on the invoice, which override + // the node_announcement. + { // Route to 1 via 2 and 3 because our channel to 1 is disabled let route = router.get_route(&node1, None, &Vec::new(), 100, 42).unwrap(); assert_eq!(route.hops.len(), 3); @@ -1449,16 +1614,22 @@ mod tests { assert_eq!(route.hops[0].short_channel_id, 2); assert_eq!(route.hops[0].fee_msat, 200); assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2)); + assert_eq!(route.hops[0].channel_features.le_flags(), &id_to_feature_flags!(2)); assert_eq!(route.hops[1].pubkey, node3); assert_eq!(route.hops[1].short_channel_id, 4); assert_eq!(route.hops[1].fee_msat, 100); assert_eq!(route.hops[1].cltv_expiry_delta, (3 << 8) | 2); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4)); assert_eq!(route.hops[2].pubkey, node1); assert_eq!(route.hops[2].short_channel_id, 3); assert_eq!(route.hops[2].fee_msat, 100); assert_eq!(route.hops[2].cltv_expiry_delta, 42); + assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(1)); + assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(3)); } { // If we specify a channel to node8, that overrides our local channel view and that gets used @@ -1466,6 +1637,7 @@ mod tests { channel_id: [0; 32], short_channel_id: Some(42), remote_network_id: node8.clone(), + counterparty_features: InitFeatures::from_le_bytes(vec![0b11]), channel_value_satoshis: 0, user_id: 0, outbound_capacity_msat: 0, @@ -1479,11 +1651,15 @@ mod tests { assert_eq!(route.hops[0].short_channel_id, 42); assert_eq!(route.hops[0].fee_msat, 200); assert_eq!(route.hops[0].cltv_expiry_delta, (13 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]); + assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion assert_eq!(route.hops[1].pubkey, node3); assert_eq!(route.hops[1].short_channel_id, 13); assert_eq!(route.hops[1].fee_msat, 100); assert_eq!(route.hops[1].cltv_expiry_delta, 42); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(13)); } let mut last_hops = vec!(RouteHint { @@ -1517,26 +1693,38 @@ mod tests { assert_eq!(route.hops[0].short_channel_id, 2); assert_eq!(route.hops[0].fee_msat, 100); assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2)); + assert_eq!(route.hops[0].channel_features.le_flags(), &id_to_feature_flags!(2)); assert_eq!(route.hops[1].pubkey, node3); assert_eq!(route.hops[1].short_channel_id, 4); assert_eq!(route.hops[1].fee_msat, 0); assert_eq!(route.hops[1].cltv_expiry_delta, (6 << 8) | 1); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4)); assert_eq!(route.hops[2].pubkey, node5); assert_eq!(route.hops[2].short_channel_id, 6); assert_eq!(route.hops[2].fee_msat, 0); assert_eq!(route.hops[2].cltv_expiry_delta, (11 << 8) | 1); + assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(5)); + assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(6)); assert_eq!(route.hops[3].pubkey, node4); assert_eq!(route.hops[3].short_channel_id, 11); assert_eq!(route.hops[3].fee_msat, 0); assert_eq!(route.hops[3].cltv_expiry_delta, (8 << 8) | 1); + // If we have a peer in the node map, we'll use their features here since we don't have + // a way of figuring out their features from the invoice: + assert_eq!(route.hops[3].node_features.le_flags(), &id_to_feature_flags!(4)); + assert_eq!(route.hops[3].channel_features.le_flags(), &id_to_feature_flags!(11)); assert_eq!(route.hops[4].pubkey, node7); assert_eq!(route.hops[4].short_channel_id, 8); assert_eq!(route.hops[4].fee_msat, 100); assert_eq!(route.hops[4].cltv_expiry_delta, 42); + assert_eq!(route.hops[4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet + assert_eq!(route.hops[4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly } { // Simple test with outbound channel to 4 to test that last_hops and first_hops connect @@ -1544,6 +1732,7 @@ mod tests { channel_id: [0; 32], short_channel_id: Some(42), remote_network_id: node4.clone(), + counterparty_features: InitFeatures::from_le_bytes(vec![0b11]), channel_value_satoshis: 0, user_id: 0, outbound_capacity_msat: 0, @@ -1557,11 +1746,15 @@ mod tests { assert_eq!(route.hops[0].short_channel_id, 42); assert_eq!(route.hops[0].fee_msat, 0); assert_eq!(route.hops[0].cltv_expiry_delta, (8 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &vec![0b11]); + assert_eq!(route.hops[0].channel_features.le_flags(), &Vec::new()); // No feature flags will meet the relevant-to-channel conversion assert_eq!(route.hops[1].pubkey, node7); assert_eq!(route.hops[1].short_channel_id, 8); assert_eq!(route.hops[1].fee_msat, 100); assert_eq!(route.hops[1].cltv_expiry_delta, 42); + assert_eq!(route.hops[1].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet + assert_eq!(route.hops[1].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly } last_hops[0].fee_base_msat = 1000; @@ -1574,21 +1767,31 @@ mod tests { assert_eq!(route.hops[0].short_channel_id, 2); assert_eq!(route.hops[0].fee_msat, 200); // fee increased as its % of value transferred across node assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2)); + assert_eq!(route.hops[0].channel_features.le_flags(), &id_to_feature_flags!(2)); assert_eq!(route.hops[1].pubkey, node3); assert_eq!(route.hops[1].short_channel_id, 4); assert_eq!(route.hops[1].fee_msat, 100); assert_eq!(route.hops[1].cltv_expiry_delta, (7 << 8) | 1); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4)); assert_eq!(route.hops[2].pubkey, node6); assert_eq!(route.hops[2].short_channel_id, 7); assert_eq!(route.hops[2].fee_msat, 0); assert_eq!(route.hops[2].cltv_expiry_delta, (10 << 8) | 1); + // If we have a peer in the node map, we'll use their features here since we don't have + // a way of figuring out their features from the invoice: + assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(6)); + assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(7)); assert_eq!(route.hops[3].pubkey, node7); assert_eq!(route.hops[3].short_channel_id, 10); assert_eq!(route.hops[3].fee_msat, 100); assert_eq!(route.hops[3].cltv_expiry_delta, 42); + assert_eq!(route.hops[3].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet + assert_eq!(route.hops[3].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly } { // ...but still use 8 for larger payments as 6 has a variable feerate @@ -1599,26 +1802,38 @@ mod tests { assert_eq!(route.hops[0].short_channel_id, 2); assert_eq!(route.hops[0].fee_msat, 3000); assert_eq!(route.hops[0].cltv_expiry_delta, (4 << 8) | 1); + assert_eq!(route.hops[0].node_features.le_flags(), &id_to_feature_flags!(2)); + assert_eq!(route.hops[0].channel_features.le_flags(), &id_to_feature_flags!(2)); assert_eq!(route.hops[1].pubkey, node3); assert_eq!(route.hops[1].short_channel_id, 4); assert_eq!(route.hops[1].fee_msat, 0); assert_eq!(route.hops[1].cltv_expiry_delta, (6 << 8) | 1); + assert_eq!(route.hops[1].node_features.le_flags(), &id_to_feature_flags!(3)); + assert_eq!(route.hops[1].channel_features.le_flags(), &id_to_feature_flags!(4)); assert_eq!(route.hops[2].pubkey, node5); assert_eq!(route.hops[2].short_channel_id, 6); assert_eq!(route.hops[2].fee_msat, 0); assert_eq!(route.hops[2].cltv_expiry_delta, (11 << 8) | 1); + assert_eq!(route.hops[2].node_features.le_flags(), &id_to_feature_flags!(5)); + assert_eq!(route.hops[2].channel_features.le_flags(), &id_to_feature_flags!(6)); assert_eq!(route.hops[3].pubkey, node4); assert_eq!(route.hops[3].short_channel_id, 11); assert_eq!(route.hops[3].fee_msat, 1000); assert_eq!(route.hops[3].cltv_expiry_delta, (8 << 8) | 1); + // If we have a peer in the node map, we'll use their features here since we don't have + // a way of figuring out their features from the invoice: + assert_eq!(route.hops[3].node_features.le_flags(), &id_to_feature_flags!(4)); + assert_eq!(route.hops[3].channel_features.le_flags(), &id_to_feature_flags!(11)); assert_eq!(route.hops[4].pubkey, node7); assert_eq!(route.hops[4].short_channel_id, 8); assert_eq!(route.hops[4].fee_msat, 2000); assert_eq!(route.hops[4].cltv_expiry_delta, 42); + assert_eq!(route.hops[4].node_features.le_flags(), &Vec::new()); // We dont pass flags in from invoices yet + assert_eq!(route.hops[4].channel_features.le_flags(), &Vec::new()); // We can't learn any flags from invoices, sadly } { // Test Router serialization/deserialization @@ -1630,4 +1845,17 @@ mod tests { assert!(::read(&mut ::std::io::Cursor::new(&w.0)).unwrap() == *network); } } + + #[test] + fn request_full_sync_finite_times() { + let (secp_ctx, _, router) = create_router(); + let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap()[..]).unwrap()); + + assert!(router.should_request_full_sync(&node_id)); + assert!(router.should_request_full_sync(&node_id)); + assert!(router.should_request_full_sync(&node_id)); + assert!(router.should_request_full_sync(&node_id)); + assert!(router.should_request_full_sync(&node_id)); + assert!(!router.should_request_full_sync(&node_id)); + } }