pub(super) pending_msg_events: Vec<events::MessageSendEvent>,
}
+/// State we hold per-peer. In the future we should put channels in here, but for now we only hold
+/// the latest Init features we heard from the peer.
+struct PeerState {
+ latest_features: InitFeatures,
+}
+
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
const ERR: () = "You need at least 32 bit pointers (well, usize, but we'll assume they're the same) for ChannelManager::latest_block_height";
channel_state: Mutex<ChannelHolder<ChanSigner>>,
our_network_key: SecretKey,
+ /// The bulk of our storage will eventually be here (channels and message queues and the like).
+ /// If we are connected to a peer we always at least have an entry here, even if no channels
+ /// are currently open with that peer.
+ /// Because adding or removing an entry is rare, we usually take an outer read lock and then
+ /// operate on the inner value freely. Sadly, this prevents parallel operation when opening a
+ /// new channel.
+ per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>,
+
pending_events: Mutex<Vec<events::Event>>,
/// Used when we have to take a BIG lock to make sure everything is self-consistent.
/// Essentially just when we're serializing ourselves out.
pub short_channel_id: Option<u64>,
/// The node_id of our counterparty
pub remote_network_id: PublicKey,
+ /// The Features the channel counterparty provided upon last connection.
+ /// Useful for routing as it is the most up-to-date copy of the counterparty's features and
+ /// many routing-relevant features are present in the init context.
+ pub counterparty_features: InitFeatures,
/// The value, in satoshis, of this channel as appears in the funding output
pub channel_value_satoshis: u64,
/// The user_id passed in to create_channel, or 0 if the channel was inbound.
}),
our_network_key: keys_manager.get_node_secret(),
+ per_peer_state: RwLock::new(HashMap::new()),
+
pending_events: Mutex::new(Vec::new()),
total_consistency_lock: RwLock::new(()),
Ok(())
}
- /// Gets the list of open channels, in random order. See ChannelDetail field documentation for
- /// more information.
- pub fn list_channels(&self) -> Vec<ChannelDetails> {
- let channel_state = self.channel_state.lock().unwrap();
- let mut res = Vec::with_capacity(channel_state.by_id.len());
- for (channel_id, channel) in channel_state.by_id.iter() {
- let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
- res.push(ChannelDetails {
- channel_id: (*channel_id).clone(),
- short_channel_id: channel.get_short_channel_id(),
- remote_network_id: channel.get_their_node_id(),
- channel_value_satoshis: channel.get_value_satoshis(),
- inbound_capacity_msat,
- outbound_capacity_msat,
- user_id: channel.get_user_id(),
- is_live: channel.is_live(),
- });
- }
- res
- }
-
- /// Gets the list of usable channels, in random order. Useful as an argument to
- /// Router::get_route to ensure non-announced channels are used.
- ///
- /// These are guaranteed to have their is_live value set to true, see the documentation for
- /// ChannelDetails::is_live for more info on exactly what the criteria are.
- pub fn list_usable_channels(&self) -> Vec<ChannelDetails> {
- let channel_state = self.channel_state.lock().unwrap();
- let mut res = Vec::with_capacity(channel_state.by_id.len());
- for (channel_id, channel) in channel_state.by_id.iter() {
- // Note we use is_live here instead of usable which leads to somewhat confused
- // internal/external nomenclature, but that's ok cause that's probably what the user
- // really wanted anyway.
- if channel.is_live() {
+ fn list_channels_with_filter<F: FnMut(&(&[u8; 32], &Channel<ChanSigner>)) -> bool>(&self, f: F) -> Vec<ChannelDetails> {
+ let mut res = Vec::new();
+ {
+ let channel_state = self.channel_state.lock().unwrap();
+ res.reserve(channel_state.by_id.len());
+ for (channel_id, channel) in channel_state.by_id.iter().filter(f) {
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
res.push(ChannelDetails {
channel_id: (*channel_id).clone(),
short_channel_id: channel.get_short_channel_id(),
remote_network_id: channel.get_their_node_id(),
+ counterparty_features: InitFeatures::empty(),
channel_value_satoshis: channel.get_value_satoshis(),
inbound_capacity_msat,
outbound_capacity_msat,
user_id: channel.get_user_id(),
- is_live: true,
+ is_live: channel.is_live(),
});
}
}
+ let per_peer_state = self.per_peer_state.read().unwrap();
+ for chan in res.iter_mut() {
+ if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) {
+ chan.counterparty_features = peer_state.lock().unwrap().latest_features.clone();
+ }
+ }
res
}
+ /// Gets the list of open channels, in random order. See ChannelDetail field documentation for
+ /// more information.
+ pub fn list_channels(&self) -> Vec<ChannelDetails> {
+ self.list_channels_with_filter(|_| true)
+ }
+
+ /// Gets the list of usable channels, in random order. Useful as an argument to
+ /// Router::get_route to ensure non-announced channels are used.
+ ///
+ /// These are guaranteed to have their is_live value set to true, see the documentation for
+ /// ChannelDetails::is_live for more info on exactly what the criteria are.
+ pub fn list_usable_channels(&self) -> Vec<ChannelDetails> {
+ // Note we use is_live here instead of usable which leads to somewhat confused
+ // internal/external nomenclature, but that's ok cause that's probably what the user
+ // really wanted anyway.
+ self.list_channels_with_filter(|&(_, ref channel)| channel.is_live())
+ }
+
/// Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs
/// will be accepted on the given channel, and after additional timeout/the closing of all
/// pending HTLCs, the channel will be closed on chain.
let _ = self.total_consistency_lock.read().unwrap();
let mut failed_channels = Vec::new();
let mut failed_payments = Vec::new();
+ let mut no_channels_remain = true;
{
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
short_to_id.remove(&short_id);
}
return false;
+ } else {
+ no_channels_remain = false;
}
}
true
}
});
}
+ if no_channels_remain {
+ self.per_peer_state.write().unwrap().remove(their_node_id);
+ }
+
for failure in failed_channels.drain(..) {
self.finish_force_close_channel(failure);
}
}
}
- fn peer_connected(&self, their_node_id: &PublicKey) {
+ fn peer_connected(&self, their_node_id: &PublicKey, init_msg: &msgs::Init) {
log_debug!(self, "Generating channel_reestablish events for {}", log_pubkey!(their_node_id));
let _ = self.total_consistency_lock.read().unwrap();
+
+ {
+ let mut peer_state_lock = self.per_peer_state.write().unwrap();
+ match peer_state_lock.entry(their_node_id.clone()) {
+ hash_map::Entry::Vacant(e) => {
+ e.insert(Mutex::new(PeerState {
+ latest_features: init_msg.features.clone(),
+ }));
+ },
+ hash_map::Entry::Occupied(e) => {
+ e.get().lock().unwrap().latest_features = init_msg.features.clone();
+ },
+ }
+ }
+
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
let pending_msg_events = &mut channel_state.pending_msg_events;
}
}
+ let per_peer_state = self.per_peer_state.write().unwrap();
+ (per_peer_state.len() as u64).write(writer)?;
+ for (peer_pubkey, peer_state_mutex) in per_peer_state.iter() {
+ peer_pubkey.write(writer)?;
+ let peer_state = peer_state_mutex.lock().unwrap();
+ peer_state.latest_features.write(writer)?;
+ }
+
Ok(())
}
}
claimable_htlcs.insert(payment_hash, previous_hops);
}
+ let peer_count: u64 = Readable::read(reader)?;
+ let mut per_peer_state = HashMap::with_capacity(cmp::min(peer_count as usize, 128));
+ for _ in 0..peer_count {
+ let peer_pubkey = Readable::read(reader)?;
+ let peer_state = PeerState {
+ latest_features: Readable::read(reader)?,
+ };
+ per_peer_state.insert(peer_pubkey, Mutex::new(peer_state));
+ }
+
let channel_manager = ChannelManager {
genesis_hash,
fee_estimator: args.fee_estimator,
}),
our_network_key: args.keys_manager.get_node_secret(),
+ per_peer_state: RwLock::new(per_peer_state),
+
pending_events: Mutex::new(Vec::new()),
total_consistency_lock: RwLock::new(()),
keys_manager: args.keys_manager,
use ln::channel::{Channel, ChannelError};
use ln::onion_utils;
use ln::router::{Route, RouteHop};
-use ln::features::{ChannelFeatures, InitFeatures};
+use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction};
use util::enforcing_trait_impls::EnforcingChannelKeys;
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let node_0_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let node_1_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &node_0_reestablish);
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let node_0_2nd_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
if recv_count == 0 {
// If all closing_signeds weren't delivered we can just resume where we left off...
let node_1_2nd_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
let mut hops = Vec::with_capacity(3);
hops.push(RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
+ node_features: NodeFeatures::empty(),
short_channel_id: chan_2.0.contents.short_channel_id,
+ channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_3.0.contents.cltv_expiry_delta as u32
});
hops.push(RouteHop {
pubkey: nodes[3].node.get_our_node_id(),
+ node_features: NodeFeatures::empty(),
short_channel_id: chan_3.0.contents.short_channel_id,
+ channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_4.1.contents.cltv_expiry_delta as u32
});
hops.push(RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
+ node_features: NodeFeatures::empty(),
short_channel_id: chan_4.0.contents.short_channel_id,
+ channel_features: ChannelFeatures::empty(),
fee_msat: 1000000,
cltv_expiry_delta: TEST_FINAL_CLTV,
});
let mut hops = Vec::with_capacity(3);
hops.push(RouteHop {
pubkey: nodes[3].node.get_our_node_id(),
+ node_features: NodeFeatures::empty(),
short_channel_id: chan_4.0.contents.short_channel_id,
+ channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_3.1.contents.cltv_expiry_delta as u32
});
hops.push(RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
+ node_features: NodeFeatures::empty(),
short_channel_id: chan_3.0.contents.short_channel_id,
+ channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_2.1.contents.cltv_expiry_delta as u32
});
hops.push(RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
+ node_features: NodeFeatures::empty(),
short_channel_id: chan_2.0.contents.short_channel_id,
+ channel_features: ChannelFeatures::empty(),
fee_msat: 1000000,
cltv_expiry_delta: TEST_FINAL_CLTV,
});
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 1);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 1);
assert_eq!(nodes[0].node.list_channels().len(), 1);
check_added_monitors!(nodes[0], 1);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &reestablish_1[0]);
//... and we can even still claim the payment!
claim_payment(&nodes[2], &[&nodes[0], &nodes[1]], our_payment_preimage, 1_000_000);
- nodes[3].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[3].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish = get_event_msg!(nodes[3], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
- nodes[0].node.peer_connected(&nodes[3].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[3].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
nodes[0].node.handle_channel_reestablish(&nodes[3].node.get_our_node_id(), &reestablish);
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(msg_events.len(), 1);
//Disconnect and Reconnect
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 1);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 1);
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &reestablish_2[0]);
check_added_monitors!(nodes[0], 1);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id());
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_0 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
}
}
// Reconnect peers
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id());
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 3);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id());
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 3);
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
(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)?;
}
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)?,
});
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});
// 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);
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,
})
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,
}
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);
}
}
// 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);
+ 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, $fee_to_target_msat);
+ add_entry!(chan_id, chan.two_to_one.src_node_id, chan.one_to_two, chan.features, $fee_to_target_msat);
}
}
}
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);
}
}
}
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}),
use chain::chaininterface;
use ln::channelmanager;
use ln::router::{Router,NodeInfo,NetworkMap,ChannelInfo,DirectionalChannelInfo,RouteHint};
- use ln::features::{ChannelFeatures, NodeFeatures};
+ use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use ln::msgs::{LightningError, ErrorAction};
use util::test_utils;
use util::test_utils::TestVecWriter;
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();
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: NodeFeatures::empty(),
+ features: NodeFeatures::from_le_bytes(id_to_feature_flags!(1)),
last_update: 1,
rgb: [0; 3],
alias: [0; 32],
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(1, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(1)),
one_to_two: DirectionalChannelInfo {
src_node_id: our_id.clone(),
last_update: 0,
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: NodeFeatures::empty(),
+ features: NodeFeatures::from_le_bytes(id_to_feature_flags!(2)),
last_update: 1,
rgb: [0; 3],
alias: [0; 32],
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(2, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(2)),
one_to_two: DirectionalChannelInfo {
src_node_id: our_id.clone(),
last_update: 0,
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: NodeFeatures::empty(),
+ features: NodeFeatures::from_le_bytes(id_to_feature_flags!(8)),
last_update: 1,
rgb: [0; 3],
alias: [0; 32],
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(12, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(12)),
one_to_two: DirectionalChannelInfo {
src_node_id: our_id.clone(),
last_update: 0,
NetworkMap::get_key(7, zero_hash.clone())),
lowest_inbound_channel_fee_base_msat: 0,
lowest_inbound_channel_fee_proportional_millionths: 0,
- features: NodeFeatures::empty(),
+ features: NodeFeatures::from_le_bytes(id_to_feature_flags!(3)),
last_update: 1,
rgb: [0; 3],
alias: [0; 32],
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(3, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(3)),
one_to_two: DirectionalChannelInfo {
src_node_id: node1.clone(),
last_update: 0,
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(4, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(4)),
one_to_two: DirectionalChannelInfo {
src_node_id: node2.clone(),
last_update: 0,
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(13, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(13)),
one_to_two: DirectionalChannelInfo {
src_node_id: node8.clone(),
last_update: 0,
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: NodeFeatures::empty(),
+ features: NodeFeatures::from_le_bytes(id_to_feature_flags!(4)),
last_update: 1,
rgb: [0; 3],
alias: [0; 32],
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(5, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(5)),
one_to_two: DirectionalChannelInfo {
src_node_id: node3.clone(),
last_update: 0,
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: NodeFeatures::empty(),
+ features: NodeFeatures::from_le_bytes(id_to_feature_flags!(5)),
last_update: 1,
rgb: [0; 3],
alias: [0; 32],
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(6, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(6)),
one_to_two: DirectionalChannelInfo {
src_node_id: node3.clone(),
last_update: 0,
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(11, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(11)),
one_to_two: DirectionalChannelInfo {
src_node_id: node5.clone(),
last_update: 0,
channels: vec!(NetworkMap::get_key(7, zero_hash.clone())),
lowest_inbound_channel_fee_base_msat: 0,
lowest_inbound_channel_fee_proportional_millionths: 0,
- features: NodeFeatures::empty(),
+ features: NodeFeatures::from_le_bytes(id_to_feature_flags!(6)),
last_update: 1,
rgb: [0; 3],
alias: [0; 32],
announcement_message: None,
});
network.channels.insert(NetworkMap::get_key(7, zero_hash.clone()), ChannelInfo {
- features: ChannelFeatures::empty(),
+ features: ChannelFeatures::from_le_bytes(id_to_feature_flags!(7)),
one_to_two: DirectionalChannelInfo {
src_node_id: node3.clone(),
last_update: 0,
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
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,
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
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,
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
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
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,
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 {
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
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,
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;
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
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