//! The top-level network map tracking logic lives here.
use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
-use bitcoin::secp256k1::key::PublicKey;
+use bitcoin::secp256k1::PublicKey;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1;
pub fn from_pubkey(pubkey: &PublicKey) -> Self {
NodeId(pubkey.serialize())
}
-
+
/// Get the public key slice from this NodeId
pub fn as_slice(&self) -> &[u8] {
&self.0
/// Update to the [`NetworkGraph`] based on payment failure information conveyed via the Onion
/// return packet by a node along the route. See [BOLT #4] for details.
///
-/// [BOLT #4]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md
+/// [BOLT #4]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md
#[derive(Clone, Debug, PartialEq)]
pub enum NetworkUpdate {
/// An error indicating a `channel_update` messages should be applied via
macro_rules! secp_verify_sig {
( $secp_ctx: expr, $msg: expr, $sig: expr, $pubkey: expr, $msg_type: expr ) => {
- match $secp_ctx.verify($msg, $sig, $pubkey) {
+ match $secp_ctx.verify_ecdsa($msg, $sig, $pubkey) {
Ok(_) => {},
Err(_) => {
return Err(LightningError {
return ();
}
- // Send a gossip_timestamp_filter to enable gossip message receipt. Note that we have to
- // use a "all timestamps" filter as sending the current timestamp would result in missing
- // gossip messages that are simply sent late. We could calculate the intended filter time
- // by looking at the current time and subtracting two weeks (before which we'll reject
- // messages), but there's not a lot of reason to bother - our peers should be discarding
- // the same messages.
+ // The lightning network's gossip sync system is completely broken in numerous ways.
+ //
+ // Given no broadly-available set-reconciliation protocol, the only reasonable approach is
+ // to do a full sync from the first few peers we connect to, and then receive gossip
+ // updates from all our peers normally.
+ //
+ // Originally, we could simply tell a peer to dump us the entire gossip table on startup,
+ // wasting lots of bandwidth but ensuring we have the full network graph. After the initial
+ // dump peers would always send gossip and we'd stay up-to-date with whatever our peer has
+ // seen.
+ //
+ // In order to reduce the bandwidth waste, "gossip queries" were introduced, allowing you
+ // to ask for the SCIDs of all channels in your peer's routing graph, and then only request
+ // channel data which you are missing. Except there was no way at all to identify which
+ // `channel_update`s you were missing, so you still had to request everything, just in a
+ // very complicated way with some queries instead of just getting the dump.
+ //
+ // Later, an option was added to fetch the latest timestamps of the `channel_update`s to
+ // make efficient sync possible, however it has yet to be implemented in lnd, which makes
+ // relying on it useless.
+ //
+ // After gossip queries were introduced, support for receiving a full gossip table dump on
+ // connection was removed from several nodes, making it impossible to get a full sync
+ // without using the "gossip queries" messages.
+ //
+ // Once you opt into "gossip queries" the only way to receive any gossip updates that a
+ // peer receives after you connect, you must send a `gossip_timestamp_filter` message. This
+ // message, as the name implies, tells the peer to not forward any gossip messages with a
+ // timestamp older than a given value (not the time the peer received the filter, but the
+ // timestamp in the update message, which is often hours behind when the peer received the
+ // message).
+ //
+ // Obnoxiously, `gossip_timestamp_filter` isn't *just* a filter, but its also a request for
+ // your peer to send you the full routing graph (subject to the filter). Thus, in order to
+ // tell a peer to send you any updates as it sees them, you have to also ask for the full
+ // routing graph to be synced. If you set a timestamp filter near the current time, peers
+ // will simply not forward any new updates they see to you which were generated some time
+ // ago (which is not uncommon). If you instead set a timestamp filter near 0 (or two weeks
+ // ago), you will always get the full routing graph from all your peers.
+ //
+ // Most lightning nodes today opt to simply turn off receiving gossip data which only
+ // propagated some time after it was generated, and, worse, often disable gossiping with
+ // several peers after their first connection. The second behavior can cause gossip to not
+ // propagate fully if there are cuts in the gossiping subgraph.
+ //
+ // In an attempt to cut a middle ground between always fetching the full graph from all of
+ // our peers and never receiving gossip from peers at all, we send all of our peers a
+ // `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 {
+ gossip_start_time -= 60 * 60 * 24 * 7 * 2; // 2 weeks ago
+ } else {
+ gossip_start_time -= 60 * 60; // an hour ago
+ }
+ }
+
let mut pending_events = self.pending_events.lock().unwrap();
pending_events.push(MessageSendEvent::SendGossipTimestampFilter {
node_id: their_node_id.clone(),
msg: GossipTimestampFilter {
chain_hash: self.network_graph.genesis_hash,
- first_timestamp: 0,
+ first_timestamp: gossip_start_time as u32, // 2106 issue!
timestamp_range: u32::max_value(),
},
});
-
- // Check if we need to perform a full synchronization with this peer
- if !self.should_request_full_sync(&their_node_id) {
- return ();
- }
-
- let first_blocknum = 0;
- let number_of_blocks = 0xffffffff;
- log_debug!(self.logger, "Sending query_channel_range peer={}, first_blocknum={}, number_of_blocks={}", log_pubkey!(their_node_id), first_blocknum, number_of_blocks);
- pending_events.push(MessageSendEvent::SendChannelRangeQuery {
- node_id: their_node_id.clone(),
- msg: QueryChannelRange {
- chain_hash: self.network_graph.genesis_hash,
- first_blocknum,
- number_of_blocks,
- },
- });
}
- /// Statelessly processes a reply to a channel range query by immediately
- /// sending an SCID query with SCIDs in the reply. To keep this handler
- /// stateless, it does not validate the sequencing of replies for multi-
- /// reply ranges. It does not validate whether the reply(ies) cover the
- /// queried range. It also does not filter SCIDs to only those in the
- /// original query range. We also do not validate that the chain_hash
- /// matches the chain_hash of the NetworkGraph. Any chan_ann message that
- /// does not match our chain_hash will be rejected when the announcement is
- /// processed.
- fn handle_reply_channel_range(&self, their_node_id: &PublicKey, msg: ReplyChannelRange) -> Result<(), LightningError> {
- log_debug!(self.logger, "Handling reply_channel_range peer={}, first_blocknum={}, number_of_blocks={}, sync_complete={}, scids={}", log_pubkey!(their_node_id), msg.first_blocknum, msg.number_of_blocks, msg.sync_complete, msg.short_channel_ids.len(),);
-
- log_debug!(self.logger, "Sending query_short_channel_ids peer={}, batch_size={}", log_pubkey!(their_node_id), msg.short_channel_ids.len());
- let mut pending_events = self.pending_events.lock().unwrap();
- pending_events.push(MessageSendEvent::SendShortIdsQuery {
- node_id: their_node_id.clone(),
- msg: QueryShortChannelIds {
- chain_hash: msg.chain_hash,
- short_channel_ids: msg.short_channel_ids,
- }
- });
-
+ fn handle_reply_channel_range(&self, _their_node_id: &PublicKey, _msg: ReplyChannelRange) -> Result<(), LightningError> {
+ // We don't make queries, so should never receive replies. If, in the future, the set
+ // reconciliation extensions to gossip queries become broadly supported, we should revert
+ // this code to its state pre-0.0.106.
Ok(())
}
- /// When an SCID query is initiated the remote peer will begin streaming
- /// gossip messages. In the event of a failure, we may have received
- /// some channel information. Before trying with another peer, the
- /// caller should update its set of SCIDs that need to be queried.
- fn handle_reply_short_channel_ids_end(&self, their_node_id: &PublicKey, msg: ReplyShortChannelIdsEnd) -> Result<(), LightningError> {
- log_debug!(self.logger, "Handling reply_short_channel_ids_end peer={}, full_information={}", log_pubkey!(their_node_id), msg.full_information);
-
- // If the remote node does not have up-to-date information for the
- // chain_hash they will set full_information=false. We can fail
- // the result and try again with a different peer.
- if !msg.full_information {
- return Err(LightningError {
- err: String::from("Received reply_short_channel_ids_end with no information"),
- action: ErrorAction::IgnoreError
- });
- }
-
+ fn handle_reply_short_channel_ids_end(&self, _their_node_id: &PublicKey, _msg: ReplyShortChannelIdsEnd) -> Result<(), LightningError> {
+ // We don't make queries, so should never receive replies. If, in the future, the set
+ // reconciliation extensions to gossip queries become broadly supported, we should revert
+ // this code to its state pre-0.0.106.
Ok(())
}
return None;
}
};
- Some((DirectedChannelInfo { channel: self, direction }, source))
+ Some((DirectedChannelInfo::new(self, direction), source))
}
/// Returns a [`DirectedChannelInfo`] for the channel directed from the given `source` to a
return None;
}
};
- Some((DirectedChannelInfo { channel: self, direction }, target))
+ Some((DirectedChannelInfo::new(self, direction), target))
+ }
+
+ /// Returns a [`ChannelUpdateInfo`] based on the direction implied by the channel_flag.
+ pub fn get_directional_info(&self, channel_flags: u8) -> Option<&ChannelUpdateInfo> {
+ let direction = channel_flags & 1u8;
+ if direction == 0 {
+ self.one_to_two.as_ref()
+ } else {
+ self.two_to_one.as_ref()
+ }
}
}
pub struct DirectedChannelInfo<'a> {
channel: &'a ChannelInfo,
direction: Option<&'a ChannelUpdateInfo>,
+ htlc_maximum_msat: u64,
+ effective_capacity: EffectiveCapacity,
}
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 capacity_msat = channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
+
+ let (htlc_maximum_msat, effective_capacity) = match (htlc_maximum_msat, capacity_msat) {
+ (Some(amount_msat), Some(capacity_msat)) => {
+ let htlc_maximum_msat = cmp::min(amount_msat, capacity_msat);
+ (htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat })
+ },
+ (Some(amount_msat), None) => {
+ (amount_msat, EffectiveCapacity::MaximumHTLC { amount_msat })
+ },
+ (None, Some(capacity_msat)) => {
+ (capacity_msat, EffectiveCapacity::Total { capacity_msat })
+ },
+ (None, None) => (EffectiveCapacity::Unknown.as_msat(), EffectiveCapacity::Unknown),
+ };
+
+ Self {
+ channel, direction, htlc_maximum_msat, effective_capacity
+ }
+ }
+
/// Returns information for the channel.
pub fn channel(&self) -> &'a ChannelInfo { self.channel }
/// Returns information for the direction.
pub fn direction(&self) -> Option<&'a ChannelUpdateInfo> { self.direction }
+ /// Returns the maximum HTLC amount allowed over the channel in the direction.
+ pub fn htlc_maximum_msat(&self) -> u64 {
+ self.htlc_maximum_msat
+ }
+
/// Returns the [`EffectiveCapacity`] of the channel in the direction.
///
/// This is either the total capacity from the funding transaction, if known, or the
/// `htlc_maximum_msat` for the direction as advertised by the gossip network, if known,
- /// whichever is smaller.
+ /// otherwise.
pub fn effective_capacity(&self) -> EffectiveCapacity {
- let capacity_msat = self.channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
- self.direction
- .and_then(|direction| direction.htlc_maximum_msat)
- .map(|max_htlc_msat| {
- let capacity_msat = capacity_msat.unwrap_or(u64::max_value());
- if max_htlc_msat < capacity_msat {
- EffectiveCapacity::MaximumHTLC { amount_msat: max_htlc_msat }
- } else {
- EffectiveCapacity::Total { capacity_msat }
- }
- })
- .or_else(|| capacity_msat.map(|capacity_msat|
- EffectiveCapacity::Total { capacity_msat }))
- .unwrap_or(EffectiveCapacity::Unknown)
+ self.effective_capacity
}
/// Returns `Some` if [`ChannelUpdateInfo`] is available in the direction.
/// Returns the [`EffectiveCapacity`] of the channel in the direction.
#[inline]
pub(super) fn effective_capacity(&self) -> EffectiveCapacity { self.inner.effective_capacity() }
+
+ /// Returns the maximum HTLC amount allowed over the channel in the direction.
+ #[inline]
+ pub(super) fn htlc_maximum_msat(&self) -> u64 { self.inner.htlc_maximum_msat() }
}
impl<'a> fmt::Debug for DirectedChannelInfoWithUpdate<'a> {
///
/// 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)]
pub enum EffectiveCapacity {
/// The available liquidity in the channel known from being a channel counterparty, and thus a
/// direct hop.
}
}
+ /// Clears the `NodeAnnouncementInfo` field for all nodes in the `NetworkGraph` for testing
+ /// purposes.
+ #[cfg(test)]
+ pub fn clear_nodes_announcement_info(&self) {
+ for node in self.nodes.write().unwrap().iter_mut() {
+ node.1.announcement_info = None;
+ }
+ }
+
/// For an already known node (from channel announcements), update its stored properties from a
/// given node announcement.
///
self.update_channel_from_unsigned_announcement_intern(msg, None, chain_access)
}
+ /// Update channel from partial announcement data received via rapid gossip sync
+ ///
+ /// `timestamp: u64`: Timestamp emulating the backdated original announcement receipt (by the
+ /// rapid gossip sync server)
+ ///
+ /// All other parameters as used in [`msgs::UnsignedChannelAnnouncement`] fields.
+ pub fn add_channel_from_partial_announcement(&self, short_channel_id: u64, timestamp: u64, features: ChannelFeatures, node_id_1: PublicKey, node_id_2: PublicKey) -> Result<(), LightningError> {
+ if node_id_1 == node_id_2 {
+ return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
+ };
+
+ let node_1 = NodeId::from_pubkey(&node_id_1);
+ let node_2 = NodeId::from_pubkey(&node_id_2);
+ let channel_info = ChannelInfo {
+ features,
+ node_one: node_1.clone(),
+ one_to_two: None,
+ node_two: node_2.clone(),
+ two_to_one: None,
+ capacity_sats: None,
+ announcement_message: None,
+ announcement_received_time: timestamp,
+ };
+
+ self.add_channel_between_nodes(short_channel_id, channel_info, None)
+ }
+
+ fn add_channel_between_nodes(&self, short_channel_id: u64, channel_info: ChannelInfo, utxo_value: Option<u64>) -> Result<(), LightningError> {
+ let mut channels = self.channels.write().unwrap();
+ let mut nodes = self.nodes.write().unwrap();
+
+ let node_id_a = channel_info.node_one.clone();
+ let node_id_b = channel_info.node_two.clone();
+
+ match channels.entry(short_channel_id) {
+ BtreeEntry::Occupied(mut entry) => {
+ //TODO: because asking the blockchain if short_channel_id is valid is only optional
+ //in the blockchain API, we need to handle it smartly here, though it's unclear
+ //exactly how...
+ if utxo_value.is_some() {
+ // Either our UTXO provider is busted, there was a reorg, or the UTXO provider
+ // only sometimes returns results. In any case remove the previous entry. Note
+ // that the spec expects us to "blacklist" the node_ids involved, but we can't
+ // do that because
+ // a) we don't *require* a UTXO provider that always returns results.
+ // b) we don't track UTXOs of channels we know about and remove them if they
+ // get reorg'd out.
+ // c) it's unclear how to do so without exposing ourselves to massive DoS risk.
+ Self::remove_channel_in_nodes(&mut nodes, &entry.get(), short_channel_id);
+ *entry.get_mut() = channel_info;
+ } else {
+ return Err(LightningError{err: "Already have knowledge of channel".to_owned(), action: ErrorAction::IgnoreDuplicateGossip});
+ }
+ },
+ BtreeEntry::Vacant(entry) => {
+ entry.insert(channel_info);
+ }
+ };
+
+ for current_node_id in [node_id_a, node_id_b].iter() {
+ match nodes.entry(current_node_id.clone()) {
+ BtreeEntry::Occupied(node_entry) => {
+ node_entry.into_mut().channels.push(short_channel_id);
+ },
+ BtreeEntry::Vacant(node_entry) => {
+ node_entry.insert(NodeInfo {
+ channels: vec!(short_channel_id),
+ lowest_inbound_channel_fees: None,
+ announcement_info: None,
+ });
+ }
+ };
+ };
+
+ Ok(())
+ }
+
fn update_channel_from_unsigned_announcement_intern<C: Deref>(
&self, msg: &msgs::UnsignedChannelAnnouncement, full_msg: Option<&msgs::ChannelAnnouncement>, chain_access: &Option<C>
) -> Result<(), LightningError>
}
let chan_info = ChannelInfo {
- features: msg.features.clone(),
- node_one: NodeId::from_pubkey(&msg.node_id_1),
- one_to_two: None,
- node_two: NodeId::from_pubkey(&msg.node_id_2),
- two_to_one: None,
- capacity_sats: utxo_value,
- announcement_message: if msg.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY
- { full_msg.cloned() } else { None },
- announcement_received_time,
- };
-
- let mut channels = self.channels.write().unwrap();
- let mut nodes = self.nodes.write().unwrap();
- match channels.entry(msg.short_channel_id) {
- BtreeEntry::Occupied(mut entry) => {
- //TODO: because asking the blockchain if short_channel_id is valid is only optional
- //in the blockchain API, we need to handle it smartly here, though it's unclear
- //exactly how...
- if utxo_value.is_some() {
- // Either our UTXO provider is busted, there was a reorg, or the UTXO provider
- // only sometimes returns results. In any case remove the previous entry. Note
- // that the spec expects us to "blacklist" the node_ids involved, but we can't
- // do that because
- // a) we don't *require* a UTXO provider that always returns results.
- // b) we don't track UTXOs of channels we know about and remove them if they
- // get reorg'd out.
- // c) it's unclear how to do so without exposing ourselves to massive DoS risk.
- Self::remove_channel_in_nodes(&mut nodes, &entry.get(), msg.short_channel_id);
- *entry.get_mut() = chan_info;
- } else {
- return Err(LightningError{err: "Already have knowledge of channel".to_owned(), action: ErrorAction::IgnoreDuplicateGossip});
- }
- },
- BtreeEntry::Vacant(entry) => {
- entry.insert(chan_info);
- }
+ features: msg.features.clone(),
+ node_one: NodeId::from_pubkey(&msg.node_id_1),
+ one_to_two: None,
+ node_two: NodeId::from_pubkey(&msg.node_id_2),
+ two_to_one: None,
+ capacity_sats: utxo_value,
+ announcement_message: if msg.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY
+ { full_msg.cloned() } else { None },
+ announcement_received_time,
};
- macro_rules! add_channel_to_node {
- ( $node_id: expr ) => {
- match nodes.entry($node_id) {
- BtreeEntry::Occupied(node_entry) => {
- node_entry.into_mut().channels.push(msg.short_channel_id);
- },
- BtreeEntry::Vacant(node_entry) => {
- node_entry.insert(NodeInfo {
- channels: vec!(msg.short_channel_id),
- lowest_inbound_channel_fees: None,
- announcement_info: None,
- });
- }
- }
- };
- }
-
- add_channel_to_node!(NodeId::from_pubkey(&msg.node_id_1));
- add_channel_to_node!(NodeId::from_pubkey(&msg.node_id_2));
-
- Ok(())
+ self.add_channel_between_nodes(msg.short_channel_id, chan_info, utxo_value)
}
/// Close a channel if a corresponding HTLC fail was sent.
/// If built with `no-std`, any updates with a timestamp more than two weeks in the past or
/// materially in the future will be rejected.
pub fn update_channel_unsigned(&self, msg: &msgs::UnsignedChannelUpdate) -> Result<(), LightningError> {
- self.update_channel_intern(msg, None, None::<(&secp256k1::Signature, &Secp256k1<secp256k1::VerifyOnly>)>)
+ self.update_channel_intern(msg, None, None::<(&secp256k1::ecdsa::Signature, &Secp256k1<secp256k1::VerifyOnly>)>)
}
- fn update_channel_intern<T: secp256k1::Verification>(&self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig_info: Option<(&secp256k1::Signature, &Secp256k1<T>)>) -> Result<(), LightningError> {
+ fn update_channel_intern<T: secp256k1::Verification>(&self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig_info: Option<(&secp256k1::ecdsa::Signature, &Secp256k1<T>)>) -> Result<(), LightningError> {
let dest_node_id;
let chan_enabled = msg.flags & (1 << 1) != (1 << 1);
let chan_was_enabled;
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use routing::network_graph::{NetGraphMsgHandler, NetworkGraph, NetworkUpdate, MAX_EXCESS_BYTES_FOR_RELAY};
use ln::msgs::{Init, OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
- UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
- ReplyChannelRange, ReplyShortChannelIdsEnd, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
+ UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
+ ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
use util::test_utils;
use util::logger::Logger;
use util::ser::{Readable, Writeable};
use hex;
- use bitcoin::secp256k1::key::{PublicKey, SecretKey};
+ use bitcoin::secp256k1::{PublicKey, SecretKey};
use bitcoin::secp256k1::{All, Secp256k1};
use io;
+ use bitcoin::secp256k1;
use prelude::*;
use sync::Arc;
f(&mut unsigned_announcement);
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
NodeAnnouncement {
- signature: secp_ctx.sign(&msghash, node_key),
+ signature: secp_ctx.sign_ecdsa(&msghash, node_key),
contents: unsigned_announcement
}
}
f(&mut unsigned_announcement);
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
ChannelAnnouncement {
- node_signature_1: secp_ctx.sign(&msghash, node_1_key),
- node_signature_2: secp_ctx.sign(&msghash, node_2_key),
- bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
- bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
+ node_signature_1: secp_ctx.sign_ecdsa(&msghash, node_1_key),
+ node_signature_2: secp_ctx.sign_ecdsa(&msghash, node_2_key),
+ bitcoin_signature_1: secp_ctx.sign_ecdsa(&msghash, node_1_btckey),
+ bitcoin_signature_2: secp_ctx.sign_ecdsa(&msghash, node_2_btckey),
contents: unsigned_announcement,
}
}
f(&mut unsigned_channel_update);
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]);
ChannelUpdate {
- signature: secp_ctx.sign(&msghash, node_key),
+ signature: secp_ctx.sign_ecdsa(&msghash, node_key),
contents: unsigned_channel_update
}
}
let fake_msghash = hash_to_message!(&zero_hash);
match net_graph_msg_handler.handle_node_announcement(
&NodeAnnouncement {
- signature: secp_ctx.sign(&fake_msghash, node_1_privkey),
+ signature: secp_ctx.sign_ecdsa(&fake_msghash, node_1_privkey),
contents: valid_announcement.contents.clone()
}) {
Ok(_) => panic!(),
}, node_1_privkey, &secp_ctx);
let zero_hash = Sha256dHash::hash(&[0; 32]);
let fake_msghash = hash_to_message!(&zero_hash);
- invalid_sig_channel_update.signature = secp_ctx.sign(&fake_msghash, node_1_privkey);
+ invalid_sig_channel_update.signature = secp_ctx.sign_ecdsa(&fake_msghash, node_1_privkey);
match net_graph_msg_handler.handle_channel_update(&invalid_sig_channel_update) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.err, "Invalid signature on channel_update message")
}
#[test]
+ #[cfg(feature = "std")]
fn calling_sync_routing_table() {
+ use std::time::{SystemTime, UNIX_EPOCH};
+
let network_graph = create_network_graph();
let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
let node_privkey_1 = &SecretKey::from_slice(&[42; 32]).unwrap();
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_privkey_1);
let chain_hash = genesis_block(Network::Testnet).header.block_hash();
- let first_blocknum = 0;
- let number_of_blocks = 0xffff_ffff;
// It should ignore if gossip_queries feature is not enabled
{
assert_eq!(events.len(), 0);
}
- // It should send a query_channel_message with the correct information
+ // It should send a gossip_timestamp_filter with the correct information
{
let init_msg = Init { features: InitFeatures::known(), remote_network_address: None };
net_graph_msg_handler.peer_connected(&node_id_1, &init_msg);
let events = net_graph_msg_handler.get_and_clear_pending_msg_events();
- assert_eq!(events.len(), 2);
+ assert_eq!(events.len(), 1);
match &events[0] {
MessageSendEvent::SendGossipTimestampFilter{ node_id, msg } => {
assert_eq!(node_id, &node_id_1);
assert_eq!(msg.chain_hash, chain_hash);
- assert_eq!(msg.first_timestamp, 0);
+ let expected_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
+ assert!((msg.first_timestamp as u64) >= expected_timestamp - 60*60*24*7*2);
+ assert!((msg.first_timestamp as u64) < expected_timestamp - 60*60*24*7*2 + 10);
assert_eq!(msg.timestamp_range, u32::max_value());
},
_ => panic!("Expected MessageSendEvent::SendChannelRangeQuery")
};
- match &events[1] {
- MessageSendEvent::SendChannelRangeQuery{ node_id, msg } => {
- assert_eq!(node_id, &node_id_1);
- assert_eq!(msg.chain_hash, chain_hash);
- assert_eq!(msg.first_blocknum, first_blocknum);
- assert_eq!(msg.number_of_blocks, number_of_blocks);
- },
- _ => panic!("Expected MessageSendEvent::SendChannelRangeQuery")
- };
- }
-
- // It should not enqueue a query when should_request_full_sync return false.
- // The initial implementation allows syncing with the first 5 peers after
- // which should_request_full_sync will return false
- {
- let network_graph = create_network_graph();
- let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
- let init_msg = Init { features: InitFeatures::known(), remote_network_address: None };
- for n in 1..7 {
- let node_privkey = &SecretKey::from_slice(&[n; 32]).unwrap();
- let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey);
- net_graph_msg_handler.peer_connected(&node_id, &init_msg);
- let events = net_graph_msg_handler.get_and_clear_pending_msg_events();
- if n <= 5 {
- assert_eq!(events.len(), 2);
- } else {
- // Even after the we stop sending the explicit query, we should still send a
- // gossip_timestamp_filter on each new connection.
- assert_eq!(events.len(), 1);
- }
-
- }
- }
- }
-
- #[test]
- fn handling_reply_channel_range() {
- let network_graph = create_network_graph();
- let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
- let node_privkey_1 = &SecretKey::from_slice(&[42; 32]).unwrap();
- let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_privkey_1);
-
- let chain_hash = genesis_block(Network::Testnet).header.block_hash();
-
- // Test receipt of a single reply that should enqueue an SCID query
- // matching the SCIDs in the reply
- {
- let result = net_graph_msg_handler.handle_reply_channel_range(&node_id_1, ReplyChannelRange {
- chain_hash,
- sync_complete: true,
- first_blocknum: 0,
- number_of_blocks: 2000,
- short_channel_ids: vec![
- 0x0003e0_000000_0000, // 992x0x0
- 0x0003e8_000000_0000, // 1000x0x0
- 0x0003e9_000000_0000, // 1001x0x0
- 0x0003f0_000000_0000, // 1008x0x0
- 0x00044c_000000_0000, // 1100x0x0
- 0x0006e0_000000_0000, // 1760x0x0
- ],
- });
- assert!(result.is_ok());
-
- // We expect to emit a query_short_channel_ids message with the received scids
- let events = net_graph_msg_handler.get_and_clear_pending_msg_events();
- assert_eq!(events.len(), 1);
- match &events[0] {
- MessageSendEvent::SendShortIdsQuery { node_id, msg } => {
- assert_eq!(node_id, &node_id_1);
- assert_eq!(msg.chain_hash, chain_hash);
- assert_eq!(msg.short_channel_ids, vec![
- 0x0003e0_000000_0000, // 992x0x0
- 0x0003e8_000000_0000, // 1000x0x0
- 0x0003e9_000000_0000, // 1001x0x0
- 0x0003f0_000000_0000, // 1008x0x0
- 0x00044c_000000_0000, // 1100x0x0
- 0x0006e0_000000_0000, // 1760x0x0
- ]);
- },
- _ => panic!("expected MessageSendEvent::SendShortIdsQuery"),
- }
- }
- }
-
- #[test]
- fn handling_reply_short_channel_ids() {
- let network_graph = create_network_graph();
- let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
- let node_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
- let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey);
-
- let chain_hash = genesis_block(Network::Testnet).header.block_hash();
-
- // Test receipt of a successful reply
- {
- let result = net_graph_msg_handler.handle_reply_short_channel_ids_end(&node_id, ReplyShortChannelIdsEnd {
- chain_hash,
- full_information: true,
- });
- assert!(result.is_ok());
- }
-
- // Test receipt of a reply that indicates the peer does not maintain up-to-date information
- // for the chain_hash requested in the query.
- {
- let result = net_graph_msg_handler.handle_reply_short_channel_ids_end(&node_id, ReplyShortChannelIdsEnd {
- chain_hash,
- full_information: false,
- });
- assert!(result.is_err());
- assert_eq!(result.err().unwrap().err, "Received reply_short_channel_ids_end with no information");
}
}