use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash;
-use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::transaction::TxOut;
-use bitcoin::blockdata::opcodes;
use bitcoin::hash_types::BlockHash;
use chain;
use chain::Access;
-use ln::features::{ChannelFeatures, NodeFeatures};
+use ln::chan_utils::make_funding_redeemscript;
+use ln::features::{ChannelFeatures, NodeFeatures, InitFeatures};
use ln::msgs::{DecodeError, ErrorAction, Init, LightningError, RoutingMessageHandler, NetAddress, MAX_VALUE_MSAT};
-use ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, OptionalField, GossipTimestampFilter};
+use ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, GossipTimestampFilter};
use ln::msgs::{QueryChannelRange, ReplyChannelRange, QueryShortChannelIds, ReplyShortChannelIdsEnd};
use ln::msgs;
-use util::ser::{Readable, ReadableArgs, Writeable, Writer};
+use util::ser::{Readable, ReadableArgs, Writeable, Writer, MaybeReadable};
use util::logger::{Logger, Level};
use util::events::{Event, EventHandler, MessageSendEvent, MessageSendEventsProvider};
use util::scid_utils::{block_from_scid, scid_from_parts, MAX_SCID_BLOCK};
use io;
+use io_extras::{copy, sink};
use prelude::*;
use alloc::collections::{BTreeMap, btree_map::Entry as BtreeEntry};
use core::{cmp, fmt};
use sync::{RwLock, RwLockReadGuard};
use core::sync::atomic::{AtomicUsize, Ordering};
use sync::Mutex;
-use core::ops::Deref;
+use core::ops::{Bound, Deref};
use bitcoin::hashes::hex::ToHex;
#[cfg(feature = "std")]
{
network_graph: G,
chain_access: Option<C>,
+ #[cfg(feature = "std")]
full_syncs_requested: AtomicUsize,
pending_events: Mutex<Vec<MessageSendEvent>>,
logger: L,
pub fn new(network_graph: G, chain_access: Option<C>, logger: L) -> Self {
P2PGossipSync {
network_graph,
+ #[cfg(feature = "std")]
full_syncs_requested: AtomicUsize::new(0),
chain_access,
pending_events: Mutex::new(vec![]),
&self.network_graph
}
+ #[cfg(feature = "std")]
/// Returns true when a full routing table sync should be performed with a peer.
fn should_request_full_sync(&self, _node_id: &PublicKey) -> bool {
//TODO: Determine whether to request a full sync based on the network map.
Ok(msg.contents.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY)
}
- fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)> {
- let mut result = Vec::with_capacity(batch_amount as usize);
+ fn get_next_channel_announcement(&self, starting_point: u64) -> Option<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)> {
let channels = self.network_graph.channels.read().unwrap();
- let mut iter = channels.range(starting_point..);
- while result.len() < batch_amount as usize {
- if let Some((_, ref chan)) = iter.next() {
- if chan.announcement_message.is_some() {
- let chan_announcement = chan.announcement_message.clone().unwrap();
- let mut one_to_two_announcement: Option<msgs::ChannelUpdate> = None;
- let mut two_to_one_announcement: Option<msgs::ChannelUpdate> = None;
- if let Some(one_to_two) = chan.one_to_two.as_ref() {
- one_to_two_announcement = one_to_two.last_update_message.clone();
- }
- if let Some(two_to_one) = chan.two_to_one.as_ref() {
- two_to_one_announcement = two_to_one.last_update_message.clone();
- }
- result.push((chan_announcement, one_to_two_announcement, two_to_one_announcement));
- } else {
- // TODO: We may end up sending un-announced channel_updates if we are sending
- // initial sync data while receiving announce/updates for this channel.
+ for (_, ref chan) in channels.range(starting_point..) {
+ if chan.announcement_message.is_some() {
+ let chan_announcement = chan.announcement_message.clone().unwrap();
+ let mut one_to_two_announcement: Option<msgs::ChannelUpdate> = None;
+ let mut two_to_one_announcement: Option<msgs::ChannelUpdate> = None;
+ if let Some(one_to_two) = chan.one_to_two.as_ref() {
+ one_to_two_announcement = one_to_two.last_update_message.clone();
}
+ if let Some(two_to_one) = chan.two_to_one.as_ref() {
+ two_to_one_announcement = two_to_one.last_update_message.clone();
+ }
+ return Some((chan_announcement, one_to_two_announcement, two_to_one_announcement));
} else {
- return result;
+ // TODO: We may end up sending un-announced channel_updates if we are sending
+ // initial sync data while receiving announce/updates for this channel.
}
}
- result
+ None
}
- fn get_next_node_announcements(&self, starting_point: Option<&PublicKey>, batch_amount: u8) -> Vec<NodeAnnouncement> {
- let mut result = Vec::with_capacity(batch_amount as usize);
+ fn get_next_node_announcement(&self, starting_point: Option<&PublicKey>) -> Option<NodeAnnouncement> {
let nodes = self.network_graph.nodes.read().unwrap();
- let mut iter = if let Some(pubkey) = starting_point {
- let mut iter = nodes.range(NodeId::from_pubkey(pubkey)..);
- iter.next();
- iter
+ let iter = if let Some(pubkey) = starting_point {
+ nodes.range((Bound::Excluded(NodeId::from_pubkey(pubkey)), Bound::Unbounded))
} else {
- nodes.range::<NodeId, _>(..)
+ nodes.range(..)
};
- while result.len() < batch_amount as usize {
- if let Some((_, ref node)) = iter.next() {
- if let Some(node_info) = node.announcement_info.as_ref() {
- if node_info.announcement_message.is_some() {
- result.push(node_info.announcement_message.clone().unwrap());
- }
+ for (_, ref node) in iter {
+ if let Some(node_info) = node.announcement_info.as_ref() {
+ if let Some(msg) = node_info.announcement_message.clone() {
+ return Some(msg);
}
- } else {
- return result;
}
}
- result
+ None
}
/// Initiates a stateless sync of routing gossip information with a peer
/// to request gossip messages for each channel. The sync is considered complete
/// when the final reply_scids_end message is received, though we are not
/// tracking this directly.
- fn peer_connected(&self, their_node_id: &PublicKey, init_msg: &Init) {
+ fn peer_connected(&self, their_node_id: &PublicKey, init_msg: &Init) -> Result<(), ()> {
// We will only perform a sync with peers that support gossip_queries.
if !init_msg.features.supports_gossip_queries() {
- return ();
+ // Don't disconnect peers for not supporting gossip queries. We may wish to have
+ // channels with peers even without being able to exchange gossip.
+ return Ok(());
}
// The lightning network's gossip sync system is completely broken in numerous ways.
// `gossip_timestamp_filter`, with the filter time set either two weeks ago or an hour ago.
//
// For no-std builds, we bury our head in the sand and do a full sync on each connection.
- let should_request_full_sync = self.should_request_full_sync(&their_node_id);
#[allow(unused_mut, unused_assignments)]
let mut gossip_start_time = 0;
#[cfg(feature = "std")]
{
gossip_start_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
- if should_request_full_sync {
+ if self.should_request_full_sync(&their_node_id) {
gossip_start_time -= 60 * 60 * 24 * 7 * 2; // 2 weeks ago
} else {
gossip_start_time -= 60 * 60; // an hour ago
timestamp_range: u32::max_value(),
},
});
+ Ok(())
}
fn handle_reply_channel_range(&self, _their_node_id: &PublicKey, _msg: ReplyChannelRange) -> Result<(), LightningError> {
action: ErrorAction::IgnoreError,
})
}
+
+ fn provided_node_features(&self) -> NodeFeatures {
+ let mut features = NodeFeatures::empty();
+ features.set_gossip_queries_optional();
+ features
+ }
+
+ fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
+ let mut features = InitFeatures::empty();
+ features.set_gossip_queries_optional();
+ features
+ }
}
impl<G: Deref<Target=NetworkGraph<L>>, C: Deref, L: Deref> MessageSendEventsProvider for P2PGossipSync<G, C, L>
/// The minimum value, which must be relayed to the next hop via the channel
pub htlc_minimum_msat: u64,
/// The maximum value which may be relayed to the next hop via the channel.
- pub htlc_maximum_msat: Option<u64>,
+ pub htlc_maximum_msat: u64,
/// Fees charged when the channel is used for routing
pub fees: RoutingFees,
/// Most recent update for the channel received from the network
}
}
-impl_writeable_tlv_based!(ChannelUpdateInfo, {
- (0, last_update, required),
- (2, enabled, required),
- (4, cltv_expiry_delta, required),
- (6, htlc_minimum_msat, required),
- (8, htlc_maximum_msat, required),
- (10, fees, required),
- (12, last_update_message, required),
-});
+impl Writeable for ChannelUpdateInfo {
+ fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ write_tlv_fields!(writer, {
+ (0, self.last_update, required),
+ (2, self.enabled, required),
+ (4, self.cltv_expiry_delta, required),
+ (6, self.htlc_minimum_msat, required),
+ // Writing htlc_maximum_msat as an Option<u64> is required to maintain backwards
+ // compatibility with LDK versions prior to v0.0.110.
+ (8, Some(self.htlc_maximum_msat), required),
+ (10, self.fees, required),
+ (12, self.last_update_message, required),
+ });
+ Ok(())
+ }
+}
+
+impl Readable for ChannelUpdateInfo {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ init_tlv_field_var!(last_update, required);
+ init_tlv_field_var!(enabled, required);
+ init_tlv_field_var!(cltv_expiry_delta, required);
+ init_tlv_field_var!(htlc_minimum_msat, required);
+ init_tlv_field_var!(htlc_maximum_msat, option);
+ init_tlv_field_var!(fees, required);
+ init_tlv_field_var!(last_update_message, required);
+
+ read_tlv_fields!(reader, {
+ (0, last_update, required),
+ (2, enabled, required),
+ (4, cltv_expiry_delta, required),
+ (6, htlc_minimum_msat, required),
+ (8, htlc_maximum_msat, required),
+ (10, fees, required),
+ (12, last_update_message, required)
+ });
+
+ if let Some(htlc_maximum_msat) = htlc_maximum_msat {
+ Ok(ChannelUpdateInfo {
+ last_update: init_tlv_based_struct_field!(last_update, required),
+ enabled: init_tlv_based_struct_field!(enabled, required),
+ cltv_expiry_delta: init_tlv_based_struct_field!(cltv_expiry_delta, required),
+ htlc_minimum_msat: init_tlv_based_struct_field!(htlc_minimum_msat, required),
+ htlc_maximum_msat,
+ fees: init_tlv_based_struct_field!(fees, required),
+ last_update_message: init_tlv_based_struct_field!(last_update_message, required),
+ })
+ } else {
+ Err(DecodeError::InvalidValue)
+ }
+ }
+}
#[derive(Clone, Debug, PartialEq)]
/// Details about a channel (both directions).
}
}
-impl_writeable_tlv_based!(ChannelInfo, {
- (0, features, required),
- (1, announcement_received_time, (default_value, 0)),
- (2, node_one, required),
- (4, one_to_two, required),
- (6, node_two, required),
- (8, two_to_one, required),
- (10, capacity_sats, required),
- (12, announcement_message, required),
-});
+impl Writeable for ChannelInfo {
+ fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ write_tlv_fields!(writer, {
+ (0, self.features, required),
+ (1, self.announcement_received_time, (default_value, 0)),
+ (2, self.node_one, required),
+ (4, self.one_to_two, required),
+ (6, self.node_two, required),
+ (8, self.two_to_one, required),
+ (10, self.capacity_sats, required),
+ (12, self.announcement_message, required),
+ });
+ Ok(())
+ }
+}
+
+// A wrapper allowing for the optional deseralization of ChannelUpdateInfo. Utilizing this is
+// necessary to maintain backwards compatibility with previous serializations of `ChannelUpdateInfo`
+// that may have no `htlc_maximum_msat` field set. In case the field is absent, we simply ignore
+// the error and continue reading the `ChannelInfo`. Hopefully, we'll then eventually receive newer
+// channel updates via the gossip network.
+struct ChannelUpdateInfoDeserWrapper(Option<ChannelUpdateInfo>);
+
+impl MaybeReadable for ChannelUpdateInfoDeserWrapper {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
+ match ::util::ser::Readable::read(reader) {
+ Ok(channel_update_option) => Ok(Some(Self(channel_update_option))),
+ Err(DecodeError::ShortRead) => Ok(None),
+ Err(DecodeError::InvalidValue) => Ok(None),
+ Err(err) => Err(err),
+ }
+ }
+}
+
+impl Readable for ChannelInfo {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ init_tlv_field_var!(features, required);
+ init_tlv_field_var!(announcement_received_time, (default_value, 0));
+ init_tlv_field_var!(node_one, required);
+ let mut one_to_two_wrap: Option<ChannelUpdateInfoDeserWrapper> = None;
+ init_tlv_field_var!(node_two, required);
+ let mut two_to_one_wrap: Option<ChannelUpdateInfoDeserWrapper> = None;
+ init_tlv_field_var!(capacity_sats, required);
+ init_tlv_field_var!(announcement_message, required);
+ read_tlv_fields!(reader, {
+ (0, features, required),
+ (1, announcement_received_time, (default_value, 0)),
+ (2, node_one, required),
+ (4, one_to_two_wrap, ignorable),
+ (6, node_two, required),
+ (8, two_to_one_wrap, ignorable),
+ (10, capacity_sats, required),
+ (12, announcement_message, required),
+ });
+
+ Ok(ChannelInfo {
+ features: init_tlv_based_struct_field!(features, required),
+ node_one: init_tlv_based_struct_field!(node_one, required),
+ one_to_two: one_to_two_wrap.map(|w| w.0).unwrap_or(None),
+ node_two: init_tlv_based_struct_field!(node_two, required),
+ two_to_one: two_to_one_wrap.map(|w| w.0).unwrap_or(None),
+ capacity_sats: init_tlv_based_struct_field!(capacity_sats, required),
+ announcement_message: init_tlv_based_struct_field!(announcement_message, required),
+ announcement_received_time: init_tlv_based_struct_field!(announcement_received_time, (default_value, 0)),
+ })
+ }
+}
/// A wrapper around [`ChannelInfo`] representing information about the channel as directed from a
/// source node to a target node.
impl<'a> DirectedChannelInfo<'a> {
#[inline]
fn new(channel: &'a ChannelInfo, direction: Option<&'a ChannelUpdateInfo>) -> Self {
- let htlc_maximum_msat = direction.and_then(|direction| direction.htlc_maximum_msat);
+ let htlc_maximum_msat = direction.map(|direction| direction.htlc_maximum_msat);
let capacity_msat = channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
let (htlc_maximum_msat, effective_capacity) = match (htlc_maximum_msat, capacity_msat) {
///
/// While this may be smaller than the actual channel capacity, amounts greater than
/// [`Self::as_msat`] should not be routed through the channel.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
pub enum EffectiveCapacity {
/// The available liquidity in the channel known from being a channel counterparty, and thus a
/// direct hop.
}
}
-impl_writeable_tlv_based!(NodeInfo, {
- (0, lowest_inbound_channel_fees, option),
- (2, announcement_info, option),
- (4, channels, vec_type),
-});
+impl Writeable for NodeInfo {
+ fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ write_tlv_fields!(writer, {
+ (0, self.lowest_inbound_channel_fees, option),
+ (2, self.announcement_info, option),
+ (4, self.channels, vec_type),
+ });
+ Ok(())
+ }
+}
+
+// A wrapper allowing for the optional deseralization of `NodeAnnouncementInfo`. Utilizing this is
+// necessary to maintain compatibility with previous serializations of `NetAddress` that have an
+// invalid hostname set. We ignore and eat all errors until we are either able to read a
+// `NodeAnnouncementInfo` or hit a `ShortRead`, i.e., read the TLV field to the end.
+struct NodeAnnouncementInfoDeserWrapper(NodeAnnouncementInfo);
+
+impl MaybeReadable for NodeAnnouncementInfoDeserWrapper {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
+ match ::util::ser::Readable::read(reader) {
+ Ok(node_announcement_info) => return Ok(Some(Self(node_announcement_info))),
+ Err(_) => {
+ copy(reader, &mut sink()).unwrap();
+ return Ok(None)
+ },
+ };
+ }
+}
+
+impl Readable for NodeInfo {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ init_tlv_field_var!(lowest_inbound_channel_fees, option);
+ let mut announcement_info_wrap: Option<NodeAnnouncementInfoDeserWrapper> = None;
+ init_tlv_field_var!(channels, vec_type);
+
+ read_tlv_fields!(reader, {
+ (0, lowest_inbound_channel_fees, option),
+ (2, announcement_info_wrap, ignorable),
+ (4, channels, vec_type),
+ });
+
+ Ok(NodeInfo {
+ lowest_inbound_channel_fees: init_tlv_based_struct_field!(lowest_inbound_channel_fees, option),
+ announcement_info: announcement_info_wrap.map(|w| w.0),
+ channels: init_tlv_based_struct_field!(channels, vec_type),
+ })
+ }
+}
const SERIALIZATION_VERSION: u8 = 1;
const MIN_SERIALIZATION_VERSION: u8 = 1;
return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
}
+ {
+ let channels = self.channels.read().unwrap();
+
+ if let Some(chan) = channels.get(&msg.short_channel_id) {
+ if chan.capacity_sats.is_some() {
+ // If we'd previously looked up the channel on-chain and checked the script
+ // against what appears on-chain, ignore the duplicate announcement.
+ //
+ // Because a reorg could replace one channel with another at the same SCID, if
+ // the channel appears to be different, we re-validate. This doesn't expose us
+ // to any more DoS risk than not, as a peer can always flood us with
+ // randomly-generated SCID values anyway.
+ //
+ // We use the Node IDs rather than the bitcoin_keys to check for "equivalence"
+ // as we didn't (necessarily) store the bitcoin keys, and we only really care
+ // if the peers on the channel changed anyway.
+ if NodeId::from_pubkey(&msg.node_id_1) == chan.node_one && NodeId::from_pubkey(&msg.node_id_2) == chan.node_two {
+ return Err(LightningError {
+ err: "Already have chain-validated channel".to_owned(),
+ action: ErrorAction::IgnoreDuplicateGossip
+ });
+ }
+ } else if chain_access.is_none() {
+ // Similarly, if we can't check the chain right now anyway, ignore the
+ // duplicate announcement without bothering to take the channels write lock.
+ return Err(LightningError {
+ err: "Already have non-chain-validated channel".to_owned(),
+ action: ErrorAction::IgnoreDuplicateGossip
+ });
+ }
+ }
+ }
+
let utxo_value = match &chain_access {
&None => {
// Tentatively accept, potentially exposing us to DoS attacks
&Some(ref chain_access) => {
match chain_access.get_utxo(&msg.chain_hash, msg.short_channel_id) {
Ok(TxOut { value, script_pubkey }) => {
- let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
- .push_slice(&msg.bitcoin_key_1.serialize())
- .push_slice(&msg.bitcoin_key_2.serialize())
- .push_opcode(opcodes::all::OP_PUSHNUM_2)
- .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh();
+ let expected_script =
+ make_funding_redeemscript(&msg.bitcoin_key_1, &msg.bitcoin_key_2).to_v0_p2wsh();
if script_pubkey != expected_script {
- return Err(LightningError{err: format!("Channel announcement key ({}) didn't match on-chain script ({})", script_pubkey.to_hex(), expected_script.to_hex()), action: ErrorAction::IgnoreError});
+ return Err(LightningError{err: format!("Channel announcement key ({}) didn't match on-chain script ({})", expected_script.to_hex(), script_pubkey.to_hex()), action: 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
match channels.get_mut(&msg.short_channel_id) {
None => return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError}),
Some(channel) => {
- if let OptionalField::Present(htlc_maximum_msat) = msg.htlc_maximum_msat {
- if htlc_maximum_msat > MAX_VALUE_MSAT {
- return Err(LightningError{err: "htlc_maximum_msat is larger than maximum possible msats".to_owned(), action: ErrorAction::IgnoreError});
- }
+ if msg.htlc_maximum_msat > MAX_VALUE_MSAT {
+ return Err(LightningError{err:
+ "htlc_maximum_msat is larger than maximum possible msats".to_owned(),
+ action: ErrorAction::IgnoreError});
+ }
- if let Some(capacity_sats) = channel.capacity_sats {
- // It's possible channel capacity is available now, although it wasn't available at announcement (so the field is None).
- // Don't query UTXO set here to reduce DoS risks.
- if capacity_sats > MAX_VALUE_MSAT / 1000 || htlc_maximum_msat > capacity_sats * 1000 {
- return Err(LightningError{err: "htlc_maximum_msat is larger than channel capacity or capacity is bogus".to_owned(), action: ErrorAction::IgnoreError});
- }
+ if let Some(capacity_sats) = channel.capacity_sats {
+ // It's possible channel capacity is available now, although it wasn't available at announcement (so the field is None).
+ // Don't query UTXO set here to reduce DoS risks.
+ if capacity_sats > MAX_VALUE_MSAT / 1000 || msg.htlc_maximum_msat > capacity_sats * 1000 {
+ return Err(LightningError{err:
+ "htlc_maximum_msat is larger than channel capacity or capacity is bogus".to_owned(),
+ action: ErrorAction::IgnoreError});
}
}
macro_rules! check_update_latest {
last_update: msg.timestamp,
cltv_expiry_delta: msg.cltv_expiry_delta,
htlc_minimum_msat: msg.htlc_minimum_msat,
- htlc_maximum_msat: if let OptionalField::Present(max_value) = msg.htlc_maximum_msat { Some(max_value) } else { None },
+ htlc_maximum_msat: msg.htlc_maximum_msat,
fees: RoutingFees {
base_msat: msg.fee_base_msat,
proportional_millionths: msg.fee_proportional_millionths,
&*self.channels
}
+ /// Returns information on a channel with the given id.
+ pub fn channel(&self, short_channel_id: u64) -> Option<&ChannelInfo> {
+ self.channels.get(&short_channel_id)
+ }
+
+ #[cfg(c_bindings)] // Non-bindings users should use `channels`
+ /// Returns the list of channels in the graph
+ pub fn list_channels(&self) -> Vec<u64> {
+ self.channels.keys().map(|c| *c).collect()
+ }
+
/// Returns all known nodes' public keys along with announced node info.
///
/// (C-not exported) because we have no mapping for `BTreeMap`s
&*self.nodes
}
+ /// Returns information on a node with the given id.
+ pub fn node(&self, node_id: &NodeId) -> Option<&NodeInfo> {
+ self.nodes.get(node_id)
+ }
+
+ #[cfg(c_bindings)] // Non-bindings users should use `nodes`
+ /// Returns the list of nodes in the graph
+ pub fn list_nodes(&self) -> Vec<NodeId> {
+ self.nodes.keys().map(|n| *n).collect()
+ }
+
/// Get network addresses by node id.
/// Returns None if the requested node is completely unknown,
/// or if node announcement for the node was never received.
#[cfg(test)]
mod tests {
use chain;
+ use ln::channelmanager;
+ use ln::chan_utils::make_funding_redeemscript;
use ln::PaymentHash;
- use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
- use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, NodeAlias, MAX_EXCESS_BYTES_FOR_RELAY};
- use ln::msgs::{Init, OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
+ use ln::features::InitFeatures;
+ use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, NodeAlias, MAX_EXCESS_BYTES_FOR_RELAY, NodeId, RoutingFees, ChannelUpdateInfo, ChannelInfo, NodeAnnouncementInfo, NodeInfo};
+ use ln::msgs::{RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
use util::test_utils;
use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
use bitcoin::blockdata::constants::genesis_block;
- use bitcoin::blockdata::script::{Builder, Script};
+ use bitcoin::blockdata::script::Script;
use bitcoin::blockdata::transaction::TxOut;
- use bitcoin::blockdata::opcodes;
use hex;
}
#[test]
+ #[cfg(feature = "std")]
fn request_full_sync_finite_times() {
let network_graph = create_network_graph();
let (secp_ctx, gossip_sync) = create_gossip_sync(&network_graph);
fn get_signed_node_announcement<F: Fn(&mut UnsignedNodeAnnouncement)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> NodeAnnouncement {
let node_id = PublicKey::from_secret_key(&secp_ctx, node_key);
let mut unsigned_announcement = UnsignedNodeAnnouncement {
- features: NodeFeatures::known(),
+ features: channelmanager::provided_node_features(),
timestamp: 100,
node_id: node_id,
rgb: [0; 3],
let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap();
let mut unsigned_announcement = UnsignedChannelAnnouncement {
- features: ChannelFeatures::known(),
+ features: channelmanager::provided_channel_features(),
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
short_channel_id: 0,
node_id_1,
}
fn get_channel_script(secp_ctx: &Secp256k1<secp256k1::All>) -> Script {
- let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap();
- let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap();
- Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
- .push_slice(&PublicKey::from_secret_key(&secp_ctx, node_1_btckey).serialize())
- .push_slice(&PublicKey::from_secret_key(&secp_ctx, node_2_btckey).serialize())
- .push_opcode(opcodes::all::OP_PUSHNUM_2)
- .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script()
- .to_v0_p2wsh()
+ let node_1_btckey = SecretKey::from_slice(&[40; 32]).unwrap();
+ let node_2_btckey = SecretKey::from_slice(&[39; 32]).unwrap();
+ make_funding_redeemscript(&PublicKey::from_secret_key(secp_ctx, &node_1_btckey),
+ &PublicKey::from_secret_key(secp_ctx, &node_2_btckey)).to_v0_p2wsh()
}
fn get_signed_channel_update<F: Fn(&mut UnsignedChannelUpdate)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelUpdate {
flags: 0,
cltv_expiry_delta: 144,
htlc_minimum_msat: 1_000_000,
- htlc_maximum_msat: OptionalField::Absent,
+ htlc_maximum_msat: 1_000_000,
fee_base_msat: 10_000,
fee_proportional_millionths: 20,
excess_data: Vec::new()
// drop new one on the floor, since we can't see any changes.
match gossip_sync.handle_channel_announcement(&valid_announcement) {
Ok(_) => panic!(),
- Err(e) => assert_eq!(e.err, "Already have knowledge of channel")
+ Err(e) => assert_eq!(e.err, "Already have non-chain-validated channel")
};
// Test if an associated transaction were not on-chain (or not confirmed).
};
}
- // If we receive announcement for the same channel (but TX is not confirmed),
- // drop new one on the floor, since we can't see any changes.
- *chain_source.utxo_ret.lock().unwrap() = Err(chain::AccessError::UnknownTx);
- match gossip_sync.handle_channel_announcement(&valid_announcement) {
- Ok(_) => panic!(),
- Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry")
- };
-
- // But if it is confirmed, replace the channel
+ // If we receive announcement for the same channel, once we've validated it against the
+ // chain, we simply ignore all new (duplicate) announcements.
*chain_source.utxo_ret.lock().unwrap() = Ok(TxOut { value: 0, script_pubkey: good_script });
- let valid_announcement = get_signed_channel_announcement(|unsigned_announcement| {
- unsigned_announcement.features = ChannelFeatures::empty();
- unsigned_announcement.short_channel_id += 2;
- }, node_1_privkey, node_2_privkey, &secp_ctx);
match gossip_sync.handle_channel_announcement(&valid_announcement) {
- Ok(res) => assert!(res),
- _ => panic!()
+ Ok(_) => panic!(),
+ Err(e) => assert_eq!(e.err, "Already have chain-validated channel")
};
- {
- match network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id) {
- Some(channel_entry) => {
- assert_eq!(channel_entry.features, ChannelFeatures::empty());
- },
- _ => panic!()
- };
- }
// Don't relay valid channels with excess data
let valid_announcement = get_signed_channel_announcement(|unsigned_announcement| {
let valid_channel_update = get_signed_channel_update(|_| {}, node_1_privkey, &secp_ctx);
match gossip_sync.handle_channel_update(&valid_channel_update) {
Ok(res) => assert!(res),
- _ => panic!()
+ _ => panic!(),
};
{
};
let valid_channel_update = get_signed_channel_update(|unsigned_channel_update| {
- unsigned_channel_update.htlc_maximum_msat = OptionalField::Present(MAX_VALUE_MSAT + 1);
+ unsigned_channel_update.htlc_maximum_msat = MAX_VALUE_MSAT + 1;
unsigned_channel_update.timestamp += 110;
}, node_1_privkey, &secp_ctx);
match gossip_sync.handle_channel_update(&valid_channel_update) {
};
let valid_channel_update = get_signed_channel_update(|unsigned_channel_update| {
- unsigned_channel_update.htlc_maximum_msat = OptionalField::Present(amount_sats * 1000 + 1);
+ unsigned_channel_update.htlc_maximum_msat = amount_sats * 1000 + 1;
unsigned_channel_update.timestamp += 110;
}, node_1_privkey, &secp_ctx);
match gossip_sync.handle_channel_update(&valid_channel_update) {
network_graph.handle_event(&Event::PaymentPathFailed {
payment_id: None,
payment_hash: PaymentHash([0; 32]),
- rejected_by_dest: false,
+ payment_failed_permanently: false,
all_paths_failed: true,
path: vec![],
network_update: Some(NetworkUpdate::ChannelUpdateMessage {
network_graph.handle_event(&Event::PaymentPathFailed {
payment_id: None,
payment_hash: PaymentHash([0; 32]),
- rejected_by_dest: false,
+ payment_failed_permanently: false,
all_paths_failed: true,
path: vec![],
network_update: Some(NetworkUpdate::ChannelFailure {
network_graph.handle_event(&Event::PaymentPathFailed {
payment_id: None,
payment_hash: PaymentHash([0; 32]),
- rejected_by_dest: false,
+ payment_failed_permanently: false,
all_paths_failed: true,
path: vec![],
network_update: Some(NetworkUpdate::ChannelFailure {
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
// Channels were not announced yet.
- let channels_with_announcements = gossip_sync.get_next_channel_announcements(0, 1);
- assert_eq!(channels_with_announcements.len(), 0);
+ let channels_with_announcements = gossip_sync.get_next_channel_announcement(0);
+ assert!(channels_with_announcements.is_none());
let short_channel_id;
{
}
// Contains initial channel announcement now.
- let channels_with_announcements = gossip_sync.get_next_channel_announcements(short_channel_id, 1);
- assert_eq!(channels_with_announcements.len(), 1);
- if let Some(channel_announcements) = channels_with_announcements.first() {
- let &(_, ref update_1, ref update_2) = channel_announcements;
+ let channels_with_announcements = gossip_sync.get_next_channel_announcement(short_channel_id);
+ if let Some(channel_announcements) = channels_with_announcements {
+ let (_, ref update_1, ref update_2) = channel_announcements;
assert_eq!(update_1, &None);
assert_eq!(update_2, &None);
} else {
panic!();
}
-
{
// Valid channel update
let valid_channel_update = get_signed_channel_update(|unsigned_channel_update| {
}
// Now contains an initial announcement and an update.
- let channels_with_announcements = gossip_sync.get_next_channel_announcements(short_channel_id, 1);
- assert_eq!(channels_with_announcements.len(), 1);
- if let Some(channel_announcements) = channels_with_announcements.first() {
- let &(_, ref update_1, ref update_2) = channel_announcements;
+ let channels_with_announcements = gossip_sync.get_next_channel_announcement(short_channel_id);
+ if let Some(channel_announcements) = channels_with_announcements {
+ let (_, ref update_1, ref update_2) = channel_announcements;
assert_ne!(update_1, &None);
assert_eq!(update_2, &None);
} else {
}
// Test that announcements with excess data won't be returned
- let channels_with_announcements = gossip_sync.get_next_channel_announcements(short_channel_id, 1);
- assert_eq!(channels_with_announcements.len(), 1);
- if let Some(channel_announcements) = channels_with_announcements.first() {
- let &(_, ref update_1, ref update_2) = channel_announcements;
+ let channels_with_announcements = gossip_sync.get_next_channel_announcement(short_channel_id);
+ if let Some(channel_announcements) = channels_with_announcements {
+ let (_, ref update_1, ref update_2) = channel_announcements;
assert_eq!(update_1, &None);
assert_eq!(update_2, &None);
} else {
}
// Further starting point have no channels after it
- let channels_with_announcements = gossip_sync.get_next_channel_announcements(short_channel_id + 1000, 1);
- assert_eq!(channels_with_announcements.len(), 0);
+ let channels_with_announcements = gossip_sync.get_next_channel_announcement(short_channel_id + 1000);
+ assert!(channels_with_announcements.is_none());
}
#[test]
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey);
// No nodes yet.
- let next_announcements = gossip_sync.get_next_node_announcements(None, 10);
- assert_eq!(next_announcements.len(), 0);
+ let next_announcements = gossip_sync.get_next_node_announcement(None);
+ assert!(next_announcements.is_none());
{
// Announce a channel to add 2 nodes
};
}
-
// Nodes were never announced
- let next_announcements = gossip_sync.get_next_node_announcements(None, 3);
- assert_eq!(next_announcements.len(), 0);
+ let next_announcements = gossip_sync.get_next_node_announcement(None);
+ assert!(next_announcements.is_none());
{
let valid_announcement = get_signed_node_announcement(|_| {}, node_1_privkey, &secp_ctx);
};
}
- let next_announcements = gossip_sync.get_next_node_announcements(None, 3);
- assert_eq!(next_announcements.len(), 2);
+ let next_announcements = gossip_sync.get_next_node_announcement(None);
+ assert!(next_announcements.is_some());
// Skip the first node.
- let next_announcements = gossip_sync.get_next_node_announcements(Some(&node_id_1), 2);
- assert_eq!(next_announcements.len(), 1);
+ let next_announcements = gossip_sync.get_next_node_announcement(Some(&node_id_1));
+ assert!(next_announcements.is_some());
{
// Later announcement which should not be relayed (excess data) prevent us from sharing a node
};
}
- let next_announcements = gossip_sync.get_next_node_announcements(Some(&node_id_1), 2);
- assert_eq!(next_announcements.len(), 0);
+ let next_announcements = gossip_sync.get_next_node_announcement(Some(&node_id_1));
+ assert!(next_announcements.is_none());
}
#[test]
#[cfg(feature = "std")]
fn calling_sync_routing_table() {
use std::time::{SystemTime, UNIX_EPOCH};
+ use ln::msgs::Init;
let network_graph = create_network_graph();
let (secp_ctx, gossip_sync) = create_gossip_sync(&network_graph);
// It should ignore if gossip_queries feature is not enabled
{
- let init_msg = Init { features: InitFeatures::known().clear_gossip_queries(), remote_network_address: None };
- gossip_sync.peer_connected(&node_id_1, &init_msg);
+ let init_msg = Init { features: InitFeatures::empty(), remote_network_address: None };
+ gossip_sync.peer_connected(&node_id_1, &init_msg).unwrap();
let events = gossip_sync.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 0);
}
// It should send a gossip_timestamp_filter with the correct information
{
- let init_msg = Init { features: InitFeatures::known(), remote_network_address: None };
- gossip_sync.peer_connected(&node_id_1, &init_msg);
+ let mut features = InitFeatures::empty();
+ features.set_gossip_queries_optional();
+ let init_msg = Init { features, remote_network_address: None };
+ gossip_sync.peer_connected(&node_id_1, &init_msg).unwrap();
let events = gossip_sync.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
match &events[0] {
assert_eq!(format_bytes_alias(b"\xFFI <heart>\0LDK!"), "\u{FFFD}I <heart>");
assert_eq!(format_bytes_alias(b"\xFFI <heart>\tLDK!"), "\u{FFFD}I <heart>\u{FFFD}LDK!");
}
+
+ #[test]
+ fn channel_info_is_readable() {
+ let chanmon_cfgs = ::ln::functional_test_utils::create_chanmon_cfgs(2);
+ let node_cfgs = ::ln::functional_test_utils::create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = ::ln::functional_test_utils::create_node_chanmgrs(2, &node_cfgs, &[None, None, None, None]);
+ let nodes = ::ln::functional_test_utils::create_network(2, &node_cfgs, &node_chanmgrs);
+
+ // 1. Test encoding/decoding of ChannelUpdateInfo
+ let chan_update_info = ChannelUpdateInfo {
+ last_update: 23,
+ enabled: true,
+ cltv_expiry_delta: 42,
+ htlc_minimum_msat: 1234,
+ htlc_maximum_msat: 5678,
+ fees: RoutingFees { base_msat: 9, proportional_millionths: 10 },
+ last_update_message: None,
+ };
+
+ let mut encoded_chan_update_info: Vec<u8> = Vec::new();
+ assert!(chan_update_info.write(&mut encoded_chan_update_info).is_ok());
+
+ // First make sure we can read ChannelUpdateInfos we just wrote
+ let read_chan_update_info: ChannelUpdateInfo = ::util::ser::Readable::read(&mut encoded_chan_update_info.as_slice()).unwrap();
+ assert_eq!(chan_update_info, read_chan_update_info);
+
+ // Check the serialization hasn't changed.
+ let legacy_chan_update_info_with_some: Vec<u8> = hex::decode("340004000000170201010402002a060800000000000004d2080909000000000000162e0a0d0c00040000000902040000000a0c0100").unwrap();
+ assert_eq!(encoded_chan_update_info, legacy_chan_update_info_with_some);
+
+ // Check we fail if htlc_maximum_msat is not present in either the ChannelUpdateInfo itself
+ // or the ChannelUpdate enclosed with `last_update_message`.
+ let legacy_chan_update_info_with_some_and_fail_update: Vec<u8> = hex::decode("b40004000000170201010402002a060800000000000004d2080909000000000000162e0a0d0c00040000000902040000000a0c8181d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f00083a840000034d013413a70000009000000000000f42400000271000000014").unwrap();
+ let read_chan_update_info_res: Result<ChannelUpdateInfo, ::ln::msgs::DecodeError> = ::util::ser::Readable::read(&mut legacy_chan_update_info_with_some_and_fail_update.as_slice());
+ assert!(read_chan_update_info_res.is_err());
+
+ let legacy_chan_update_info_with_none: Vec<u8> = hex::decode("2c0004000000170201010402002a060800000000000004d20801000a0d0c00040000000902040000000a0c0100").unwrap();
+ let read_chan_update_info_res: Result<ChannelUpdateInfo, ::ln::msgs::DecodeError> = ::util::ser::Readable::read(&mut legacy_chan_update_info_with_none.as_slice());
+ assert!(read_chan_update_info_res.is_err());
+
+ // 2. Test encoding/decoding of ChannelInfo
+ // Check we can encode/decode ChannelInfo without ChannelUpdateInfo fields present.
+ let chan_info_none_updates = ChannelInfo {
+ features: channelmanager::provided_channel_features(),
+ node_one: NodeId::from_pubkey(&nodes[0].node.get_our_node_id()),
+ one_to_two: None,
+ node_two: NodeId::from_pubkey(&nodes[1].node.get_our_node_id()),
+ two_to_one: None,
+ capacity_sats: None,
+ announcement_message: None,
+ announcement_received_time: 87654,
+ };
+
+ let mut encoded_chan_info: Vec<u8> = Vec::new();
+ assert!(chan_info_none_updates.write(&mut encoded_chan_info).is_ok());
+
+ let read_chan_info: ChannelInfo = ::util::ser::Readable::read(&mut encoded_chan_info.as_slice()).unwrap();
+ assert_eq!(chan_info_none_updates, read_chan_info);
+
+ // Check we can encode/decode ChannelInfo with ChannelUpdateInfo fields present.
+ let chan_info_some_updates = ChannelInfo {
+ features: channelmanager::provided_channel_features(),
+ node_one: NodeId::from_pubkey(&nodes[0].node.get_our_node_id()),
+ one_to_two: Some(chan_update_info.clone()),
+ node_two: NodeId::from_pubkey(&nodes[1].node.get_our_node_id()),
+ two_to_one: Some(chan_update_info.clone()),
+ capacity_sats: None,
+ announcement_message: None,
+ announcement_received_time: 87654,
+ };
+
+ let mut encoded_chan_info: Vec<u8> = Vec::new();
+ assert!(chan_info_some_updates.write(&mut encoded_chan_info).is_ok());
+
+ let read_chan_info: ChannelInfo = ::util::ser::Readable::read(&mut encoded_chan_info.as_slice()).unwrap();
+ assert_eq!(chan_info_some_updates, read_chan_info);
+
+ // Check the serialization hasn't changed.
+ let legacy_chan_info_with_some: Vec<u8> = hex::decode("ca00020000010800000000000156660221027f921585f2ac0c7c70e36110adecfd8fd14b8a99bfb3d000a283fcac358fce88043636340004000000170201010402002a060800000000000004d2080909000000000000162e0a0d0c00040000000902040000000a0c010006210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c23083636340004000000170201010402002a060800000000000004d2080909000000000000162e0a0d0c00040000000902040000000a0c01000a01000c0100").unwrap();
+ assert_eq!(encoded_chan_info, legacy_chan_info_with_some);
+
+ // Check we can decode legacy ChannelInfo, even if the `two_to_one` / `one_to_two` /
+ // `last_update_message` fields fail to decode due to missing htlc_maximum_msat.
+ let legacy_chan_info_with_some_and_fail_update = hex::decode("fd01ca00020000010800000000000156660221027f921585f2ac0c7c70e36110adecfd8fd14b8a99bfb3d000a283fcac358fce8804b6b6b40004000000170201010402002a060800000000000004d2080909000000000000162e0a0d0c00040000000902040000000a0c8181d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f00083a840000034d013413a70000009000000000000f4240000027100000001406210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c2308b6b6b40004000000170201010402002a060800000000000004d2080909000000000000162e0a0d0c00040000000902040000000a0c8181d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f00083a840000034d013413a70000009000000000000f424000002710000000140a01000c0100").unwrap();
+ let read_chan_info: ChannelInfo = ::util::ser::Readable::read(&mut legacy_chan_info_with_some_and_fail_update.as_slice()).unwrap();
+ assert_eq!(read_chan_info.announcement_received_time, 87654);
+ assert_eq!(read_chan_info.one_to_two, None);
+ assert_eq!(read_chan_info.two_to_one, None);
+
+ let legacy_chan_info_with_none: Vec<u8> = hex::decode("ba00020000010800000000000156660221027f921585f2ac0c7c70e36110adecfd8fd14b8a99bfb3d000a283fcac358fce88042e2e2c0004000000170201010402002a060800000000000004d20801000a0d0c00040000000902040000000a0c010006210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c23082e2e2c0004000000170201010402002a060800000000000004d20801000a0d0c00040000000902040000000a0c01000a01000c0100").unwrap();
+ let read_chan_info: ChannelInfo = ::util::ser::Readable::read(&mut legacy_chan_info_with_none.as_slice()).unwrap();
+ assert_eq!(read_chan_info.announcement_received_time, 87654);
+ assert_eq!(read_chan_info.one_to_two, None);
+ assert_eq!(read_chan_info.two_to_one, None);
+ }
+
+ #[test]
+ fn node_info_is_readable() {
+ use std::convert::TryFrom;
+
+ // 1. Check we can read a valid NodeAnnouncementInfo and fail on an invalid one
+ let valid_netaddr = ::ln::msgs::NetAddress::Hostname { hostname: ::util::ser::Hostname::try_from("A".to_string()).unwrap(), port: 1234 };
+ let valid_node_ann_info = NodeAnnouncementInfo {
+ features: channelmanager::provided_node_features(),
+ last_update: 0,
+ rgb: [0u8; 3],
+ alias: NodeAlias([0u8; 32]),
+ addresses: vec![valid_netaddr],
+ announcement_message: None,
+ };
+
+ let mut encoded_valid_node_ann_info = Vec::new();
+ assert!(valid_node_ann_info.write(&mut encoded_valid_node_ann_info).is_ok());
+ let read_valid_node_ann_info: NodeAnnouncementInfo = ::util::ser::Readable::read(&mut encoded_valid_node_ann_info.as_slice()).unwrap();
+ assert_eq!(read_valid_node_ann_info, valid_node_ann_info);
+
+ let encoded_invalid_node_ann_info = hex::decode("3f0009000788a000080a51a20204000000000403000000062000000000000000000000000000000000000000000000000000000000000000000a0505014004d2").unwrap();
+ let read_invalid_node_ann_info_res: Result<NodeAnnouncementInfo, ::ln::msgs::DecodeError> = ::util::ser::Readable::read(&mut encoded_invalid_node_ann_info.as_slice());
+ assert!(read_invalid_node_ann_info_res.is_err());
+
+ // 2. Check we can read a NodeInfo anyways, but set the NodeAnnouncementInfo to None if invalid
+ let valid_node_info = NodeInfo {
+ channels: Vec::new(),
+ lowest_inbound_channel_fees: None,
+ announcement_info: Some(valid_node_ann_info),
+ };
+
+ let mut encoded_valid_node_info = Vec::new();
+ assert!(valid_node_info.write(&mut encoded_valid_node_info).is_ok());
+ let read_valid_node_info: NodeInfo = ::util::ser::Readable::read(&mut encoded_valid_node_info.as_slice()).unwrap();
+ assert_eq!(read_valid_node_info, valid_node_info);
+
+ let encoded_invalid_node_info_hex = hex::decode("4402403f0009000788a000080a51a20204000000000403000000062000000000000000000000000000000000000000000000000000000000000000000a0505014004d20400").unwrap();
+ let read_invalid_node_info: NodeInfo = ::util::ser::Readable::read(&mut encoded_invalid_node_info_hex.as_slice()).unwrap();
+ assert_eq!(read_invalid_node_info.announcement_info, None);
+ }
}
#[cfg(all(test, feature = "_bench_unstable"))]
#[bench]
fn read_network_graph(bench: &mut Bencher) {
let logger = ::util::test_utils::TestLogger::new();
- let mut d = ::routing::router::test_utils::get_route_file().unwrap();
+ let mut d = ::routing::router::bench_utils::get_route_file().unwrap();
let mut v = Vec::new();
d.read_to_end(&mut v).unwrap();
bench.iter(|| {
#[bench]
fn write_network_graph(bench: &mut Bencher) {
let logger = ::util::test_utils::TestLogger::new();
- let mut d = ::routing::router::test_utils::get_route_file().unwrap();
+ let mut d = ::routing::router::bench_utils::get_route_file().unwrap();
let net_graph = NetworkGraph::read(&mut d, &logger).unwrap();
bench.iter(|| {
let _ = net_graph.encode();