impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
/// Handles any network updates originating from [`Event`]s.
+ //
+ /// Note that this will skip applying any [`NetworkUpdate::ChannelUpdateMessage`] to avoid
+ /// leaking possibly identifying information of the sender to the public network.
///
/// [`Event`]: crate::events::Event
pub fn handle_network_update(&self, network_update: &NetworkUpdate) {
let short_channel_id = msg.contents.short_channel_id;
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
- log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
- let _ = self.update_channel(msg);
+ log_debug!(self.logger, "Skipping application of a channel update from a payment failure. Channel {} is {}.", short_channel_id, status);
},
NetworkUpdate::ChannelFailure { short_channel_id, is_permanent } => {
if is_permanent {
}
}
+fn message_sha256d_hash<M: Writeable>(msg: &M) -> Sha256dHash {
+ let mut engine = Sha256dHash::engine();
+ msg.write(&mut engine).expect("In-memory structs should not fail to serialize");
+ Sha256dHash::from_engine(engine)
+}
+
/// Verifies the signature of a [`NodeAnnouncement`].
///
/// Returns an error if it is invalid.
pub fn verify_node_announcement<C: Verification>(msg: &NodeAnnouncement, secp_ctx: &Secp256k1<C>) -> Result<(), LightningError> {
- let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
+ let msg_hash = hash_to_message!(&message_sha256d_hash(&msg.contents)[..]);
secp_verify_sig!(secp_ctx, &msg_hash, &msg.signature, &get_pubkey_from_node_id!(msg.contents.node_id, "node_announcement"), "node_announcement");
Ok(())
///
/// Returns an error if one of the signatures is invalid.
pub fn verify_channel_announcement<C: Verification>(msg: &ChannelAnnouncement, secp_ctx: &Secp256k1<C>) -> Result<(), LightningError> {
- let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
+ let msg_hash = hash_to_message!(&message_sha256d_hash(&msg.contents)[..]);
secp_verify_sig!(secp_ctx, &msg_hash, &msg.node_signature_1, &get_pubkey_from_node_id!(msg.contents.node_id_1, "channel_announcement"), "channel_announcement");
secp_verify_sig!(secp_ctx, &msg_hash, &msg.node_signature_2, &get_pubkey_from_node_id!(msg.contents.node_id_2, "channel_announcement"), "channel_announcement");
secp_verify_sig!(secp_ctx, &msg_hash, &msg.bitcoin_signature_1, &get_pubkey_from_node_id!(msg.contents.bitcoin_key_1, "channel_announcement"), "channel_announcement");
///
/// Since node aliases are provided by third parties, they are a potential avenue for injection
/// attacks. Care must be taken when processing.
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct NodeAlias(pub [u8; 32]);
impl fmt::Display for NodeAlias {
let chain_hash: ChainHash = Readable::read(reader)?;
let channels_count: u64 = Readable::read(reader)?;
- let mut channels = IndexedMap::new();
+ // In Nov, 2023 there were about 15,000 nodes; we cap allocations to 1.5x that.
+ let mut channels = IndexedMap::with_capacity(cmp::min(channels_count as usize, 22500));
for _ in 0..channels_count {
let chan_id: u64 = Readable::read(reader)?;
let chan_info = Readable::read(reader)?;
channels.insert(chan_id, chan_info);
}
let nodes_count: u64 = Readable::read(reader)?;
- let mut nodes = IndexedMap::new();
+ // In Nov, 2023 there were about 69K channels; we cap allocations to 1.5x that.
+ let mut nodes = IndexedMap::with_capacity(cmp::min(nodes_count as usize, 103500));
for _ in 0..nodes_count {
let node_id = Readable::read(reader)?;
let node_info = Readable::read(reader)?;
/// For an already known (from announcement) channel, update info about one of the directions
/// of the channel.
///
- /// You probably don't want to call this directly, instead relying on a P2PGossipSync's
- /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
+ /// You probably don't want to call this directly, instead relying on a [`P2PGossipSync`]'s
+ /// [`RoutingMessageHandler`] implementation to call it indirectly. This may be useful to accept
/// routing messages from a source using a protocol other than the lightning P2P protocol.
///
/// 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(&self, msg: &msgs::ChannelUpdate) -> Result<(), LightningError> {
- self.update_channel_intern(&msg.contents, Some(&msg), Some(&msg.signature))
+ self.update_channel_internal(&msg.contents, Some(&msg), Some(&msg.signature), false)
}
/// For an already known (from announcement) channel, update info about one of the directions
/// 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)
+ self.update_channel_internal(msg, None, None, false)
}
- fn update_channel_intern(&self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig: Option<&secp256k1::ecdsa::Signature>) -> Result<(), LightningError> {
+ /// For an already known (from announcement) channel, verify the given [`ChannelUpdate`].
+ ///
+ /// This checks whether the update currently is applicable by [`Self::update_channel`].
+ ///
+ /// 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 verify_channel_update(&self, msg: &msgs::ChannelUpdate) -> Result<(), LightningError> {
+ self.update_channel_internal(&msg.contents, Some(&msg), Some(&msg.signature), true)
+ }
+
+ fn update_channel_internal(&self, msg: &msgs::UnsignedChannelUpdate,
+ full_msg: Option<&msgs::ChannelUpdate>, sig: Option<&secp256k1::ecdsa::Signature>,
+ only_verify: bool) -> Result<(), LightningError>
+ {
let chan_enabled = msg.flags & (1 << 1) != (1 << 1);
if msg.chain_hash != self.chain_hash {
} }
}
- let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
+ let msg_hash = hash_to_message!(&message_sha256d_hash(&msg)[..]);
if msg.flags & 1 == 1 {
check_update_latest!(channel.two_to_one);
if let Some(sig) = sig {
action: ErrorAction::IgnoreAndLog(Level::Debug)
})?, "channel_update");
}
- channel.two_to_one = get_new_channel_info!();
+ if !only_verify {
+ channel.two_to_one = get_new_channel_info!();
+ }
} else {
check_update_latest!(channel.one_to_two);
if let Some(sig) = sig {
action: ErrorAction::IgnoreAndLog(Level::Debug)
})?, "channel_update");
}
- channel.one_to_two = get_new_channel_info!();
+ if !only_verify {
+ channel.one_to_two = get_new_channel_info!();
+ }
}
}
}
}
let valid_channel_update = get_signed_channel_update(|_| {}, node_1_privkey, &secp_ctx);
+ network_graph.verify_channel_update(&valid_channel_update).unwrap();
match gossip_sync.handle_channel_update(&valid_channel_update) {
Ok(res) => assert!(res),
_ => panic!(),
let short_channel_id;
{
- // Announce a channel we will update
+ // Check we won't apply an update via `handle_network_update` for privacy reasons, but
+ // can continue fine if we manually apply it.
let valid_channel_announcement = get_signed_channel_announcement(|_| {}, node_1_privkey, node_2_privkey, &secp_ctx);
short_channel_id = valid_channel_announcement.contents.short_channel_id;
let chain_source: Option<&test_utils::TestChainSource> = None;
assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_none());
network_graph.handle_network_update(&NetworkUpdate::ChannelUpdateMessage {
- msg: valid_channel_update,
+ msg: valid_channel_update.clone(),
});
- assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_some());
+ assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_none());
+ network_graph.update_channel(&valid_channel_update).unwrap();
}
// Non-permanent failure doesn't touch the channel at all