Skip to content

Commit be93dc3

Browse files
authored
Merge pull request #3581 from TheBlueMatt/2025-01-rgs-speedups
Avoid parsing `PublicKey`s when handling RGS updates
2 parents a91196b + 0ff7fba commit be93dc3

File tree

3 files changed

+49
-26
lines changed

3 files changed

+49
-26
lines changed

lightning-background-processor/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,8 +2379,8 @@ mod tests {
23792379
42,
23802380
53,
23812381
features,
2382-
$nodes[0].node.get_our_node_id(),
2383-
$nodes[1].node.get_our_node_id(),
2382+
$nodes[0].node.get_our_node_id().into(),
2383+
$nodes[1].node.get_our_node_id().into(),
23842384
)
23852385
.expect("Failed to update channel from partial announcement");
23862386
let original_graph_description = $nodes[0].network_graph.to_string();

lightning-rapid-gossip-sync/src/processing.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use core::ops::Deref;
33
use core::sync::atomic::Ordering;
44

55
use bitcoin::constants::ChainHash;
6-
use bitcoin::secp256k1::PublicKey;
76

87
use lightning::io;
98
use lightning::ln::msgs::{
@@ -117,7 +116,7 @@ where
117116
};
118117

119118
let node_id_count: u32 = Readable::read(read_cursor)?;
120-
let mut node_ids: Vec<PublicKey> = Vec::with_capacity(core::cmp::min(
119+
let mut node_ids: Vec<NodeId> = Vec::with_capacity(core::cmp::min(
121120
node_id_count,
122121
MAX_INITIAL_NODE_ID_VECTOR_CAPACITY,
123122
) as usize);
@@ -154,9 +153,8 @@ where
154153
let key_parity = node_detail_flag & 0b_0000_0011;
155154
pubkey_bytes[0] = key_parity;
156155

157-
let current_pubkey = PublicKey::from_slice(&pubkey_bytes)?;
158-
let current_node_id = NodeId::from_pubkey(&current_pubkey);
159-
node_ids.push(current_pubkey);
156+
let current_node_id = NodeId::from_slice(&pubkey_bytes)?;
157+
node_ids.push(current_node_id);
160158

161159
if is_reminder || has_address_details || feature_detail_marker > 0 {
162160
let mut synthetic_node_announcement = UnsignedNodeAnnouncement {

lightning/src/routing/gossip.rs

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,12 @@ const MAX_EXCESS_BYTES_FOR_RELAY: usize = 1024;
7272
/// This value ensures a reply fits within the 65k payload limit and is consistent with other implementations.
7373
const MAX_SCIDS_PER_REPLY: usize = 8000;
7474

75-
/// Represents the compressed public key of a node
75+
/// A compressed pubkey which a node uses to sign announcements and decode HTLCs routed through it.
76+
///
77+
/// This type stores a simple byte array which is not checked for validity (i.e. that it describes
78+
/// a point which lies on the secp256k1 curve), unlike [`PublicKey`], as validity checking would
79+
/// otherwise represent a large portion of [`NetworkGraph`] deserialization time (and RGS
80+
/// application).
7681
#[derive(Clone, Copy, PartialEq, Eq)]
7782
pub struct NodeId([u8; PUBLIC_KEY_SIZE]);
7883

@@ -1660,8 +1665,7 @@ where
16601665

16611666
let chain_hash: ChainHash = Readable::read(reader)?;
16621667
let channels_count: u64 = Readable::read(reader)?;
1663-
// In Nov, 2023 there were about 15,000 nodes; we cap allocations to 1.5x that.
1664-
let mut channels = IndexedMap::with_capacity(cmp::min(channels_count as usize, 22500));
1668+
let mut channels = IndexedMap::with_capacity(CHAN_COUNT_ESTIMATE);
16651669
for _ in 0..channels_count {
16661670
let chan_id: u64 = Readable::read(reader)?;
16671671
let chan_info: ChannelInfo = Readable::read(reader)?;
@@ -1673,8 +1677,7 @@ where
16731677
if nodes_count > u32::max_value() as u64 / 2 {
16741678
return Err(DecodeError::InvalidValue);
16751679
}
1676-
// In Nov, 2023 there were about 69K channels; we cap allocations to 1.5x that.
1677-
let mut nodes = IndexedMap::with_capacity(cmp::min(nodes_count as usize, 103500));
1680+
let mut nodes = IndexedMap::with_capacity(NODE_COUNT_ESTIMATE);
16781681
for i in 0..nodes_count {
16791682
let node_id = Readable::read(reader)?;
16801683
let mut node_info: NodeInfo = Readable::read(reader)?;
@@ -1750,6 +1753,15 @@ where
17501753
}
17511754
}
17521755

1756+
// In Jan, 2025 there were about 49K channels.
1757+
// We over-allocate by a bit because 20% more is better than the double we get if we're slightly
1758+
// too low
1759+
const CHAN_COUNT_ESTIMATE: usize = 60_000;
1760+
// In Jan, 2025 there were about 15K nodes
1761+
// We over-allocate by a bit because 33% more is better than the double we get if we're slightly
1762+
// too low
1763+
const NODE_COUNT_ESTIMATE: usize = 20_000;
1764+
17531765
impl<L: Deref> NetworkGraph<L>
17541766
where
17551767
L::Target: Logger,
@@ -1760,8 +1772,8 @@ where
17601772
secp_ctx: Secp256k1::verification_only(),
17611773
chain_hash: ChainHash::using_genesis_block(network),
17621774
logger,
1763-
channels: RwLock::new(IndexedMap::new()),
1764-
nodes: RwLock::new(IndexedMap::new()),
1775+
channels: RwLock::new(IndexedMap::with_capacity(CHAN_COUNT_ESTIMATE)),
1776+
nodes: RwLock::new(IndexedMap::with_capacity(NODE_COUNT_ESTIMATE)),
17651777
next_node_counter: AtomicUsize::new(0),
17661778
removed_node_counters: Mutex::new(Vec::new()),
17671779
last_rapid_gossip_sync_timestamp: Mutex::new(None),
@@ -1992,8 +2004,8 @@ where
19922004
///
19932005
/// All other parameters as used in [`msgs::UnsignedChannelAnnouncement`] fields.
19942006
pub fn add_channel_from_partial_announcement(
1995-
&self, short_channel_id: u64, timestamp: u64, features: ChannelFeatures,
1996-
node_id_1: PublicKey, node_id_2: PublicKey,
2007+
&self, short_channel_id: u64, timestamp: u64, features: ChannelFeatures, node_id_1: NodeId,
2008+
node_id_2: NodeId,
19972009
) -> Result<(), LightningError> {
19982010
if node_id_1 == node_id_2 {
19992011
return Err(LightningError {
@@ -2002,13 +2014,11 @@ where
20022014
});
20032015
};
20042016

2005-
let node_1 = NodeId::from_pubkey(&node_id_1);
2006-
let node_2 = NodeId::from_pubkey(&node_id_2);
20072017
let channel_info = ChannelInfo {
20082018
features,
2009-
node_one: node_1.clone(),
2019+
node_one: node_id_1,
20102020
one_to_two: None,
2011-
node_two: node_2.clone(),
2021+
node_two: node_id_2,
20122022
two_to_one: None,
20132023
capacity_sats: None,
20142024
announcement_message: None,
@@ -2537,7 +2547,7 @@ where
25372547
}
25382548
};
25392549

2540-
let node_pubkey;
2550+
let mut node_pubkey = None;
25412551
{
25422552
let channels = self.channels.read().unwrap();
25432553
match channels.get(&msg.short_channel_id) {
@@ -2556,16 +2566,31 @@ where
25562566
} else {
25572567
channel.node_one.as_slice()
25582568
};
2559-
node_pubkey = PublicKey::from_slice(node_id).map_err(|_| LightningError {
2560-
err: "Couldn't parse source node pubkey".to_owned(),
2561-
action: ErrorAction::IgnoreAndLog(Level::Debug),
2562-
})?;
2569+
if sig.is_some() {
2570+
// PublicKey parsing isn't entirely trivial as it requires that we check
2571+
// that the provided point is on the curve. Thus, if we don't have a
2572+
// signature to verify, we want to skip the parsing step entirely.
2573+
// This represents a substantial speedup in applying RGS snapshots.
2574+
node_pubkey =
2575+
Some(PublicKey::from_slice(node_id).map_err(|_| LightningError {
2576+
err: "Couldn't parse source node pubkey".to_owned(),
2577+
action: ErrorAction::IgnoreAndLog(Level::Debug),
2578+
})?);
2579+
}
25632580
},
25642581
}
25652582
}
25662583

2567-
let msg_hash = hash_to_message!(&message_sha256d_hash(&msg)[..]);
25682584
if let Some(sig) = sig {
2585+
let msg_hash = hash_to_message!(&message_sha256d_hash(&msg)[..]);
2586+
let node_pubkey = if let Some(pubkey) = node_pubkey {
2587+
pubkey
2588+
} else {
2589+
debug_assert!(false, "node_pubkey should have been decoded above");
2590+
let err = "node_pubkey wasn't decoded but we need it to check a sig".to_owned();
2591+
let action = ErrorAction::IgnoreAndLog(Level::Error);
2592+
return Err(LightningError { err, action });
2593+
};
25692594
secp_verify_sig!(self.secp_ctx, &msg_hash, &sig, &node_pubkey, "channel_update");
25702595
}
25712596

0 commit comments

Comments
 (0)