Skip to content

Commit 271ac91

Browse files
committed
Enforce that node_ids are sorted in channel_announcements
We already enforced that nodes can't have a chanel with themselves, but the spec was updated to require strict ordering at lightning/bolts#1333 so we enforce this as well. Test fixes by claude.
1 parent 783c280 commit 271ac91

4 files changed

Lines changed: 67 additions & 17 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ mod tests {
549549
108, 101, 46, 99, 111, 109, 1, 187, 19, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
550550
1, 5, 57, 13, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 2, 23, 48, 62, 77, 75, 108,
551551
209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201,
552-
66, 51, 116, 196, 81, 167, 37, 77, 7, 102, 0, 0, 2, 25, 48, 0, 0, 0, 1, 0, 0, 1, 0, 1,
552+
66, 51, 116, 196, 81, 167, 37, 77, 7, 102, 0, 0, 2, 25, 48, 0, 0, 0, 1, 0, 0, 1, 1, 0,
553553
0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
554554
0, 1, 0, 0, 1,
555555
];
@@ -669,7 +669,7 @@ mod tests {
669669
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
670670
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
671671
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
672-
0, 0, 0, 1, 0, 0, 1, 0, 255, 128, 0, 0, 0, 0, 0, 0, 1, 0, 147, 42, 23, 23, 23, 23, 23,
672+
0, 0, 0, 1, 0, 0, 1, 1, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 147, 42, 23, 23, 23, 23, 23,
673673
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
674674
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
675675
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,

lightning/src/routing/gossip.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,9 +2014,9 @@ impl<L: Logger> NetworkGraph<L> {
20142014
&self, short_channel_id: u64, capacity_sats: Option<u64>, timestamp: u64,
20152015
features: ChannelFeatures, node_id_1: NodeId, node_id_2: NodeId,
20162016
) -> Result<(), LightningError> {
2017-
if node_id_1 == node_id_2 {
2017+
if node_id_1 >= node_id_2 {
20182018
return Err(LightningError {
2019-
err: "Channel announcement node had a channel with itself".to_owned(),
2019+
err: "node_ids in channel_announcements must be sorted".to_owned(),
20202020
action: ErrorAction::IgnoreError,
20212021
});
20222022
};
@@ -2123,6 +2123,13 @@ impl<L: Logger> NetworkGraph<L> {
21232123
) -> Result<(), LightningError> {
21242124
let channels = self.channels.read().unwrap();
21252125

2126+
if msg.node_id_1 >= msg.node_id_2 {
2127+
return Err(LightningError {
2128+
err: "node_ids in channel_announcements must be sorted".to_owned(),
2129+
action: ErrorAction::IgnoreError,
2130+
});
2131+
}
2132+
21262133
if let Some(chan) = channels.get(&msg.short_channel_id) {
21272134
if chan.capacity_sats.is_some() {
21282135
// If we'd previously looked up the channel on-chain and checked the script
@@ -3126,7 +3133,7 @@ pub(crate) mod tests {
31263133
.handle_channel_announcement(Some(node_1_pubkey), &channel_to_itself_announcement)
31273134
{
31283135
Ok(_) => panic!(),
3129-
Err(e) => assert_eq!(e.err, "Channel announcement node had a channel with itself"),
3136+
Err(e) => assert_eq!(e.err, "node_ids in channel_announcements must be sorted"),
31303137
};
31313138

31323139
// Test that channel announcements with the wrong chain hash are ignored (network graph is testnet,

lightning/src/routing/scoring.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,20 +2705,28 @@ mod tests {
27052705
let node_1_secret = &SecretKey::from_slice(&[39; 32]).unwrap();
27062706
let node_2_secret = &SecretKey::from_slice(&[40; 32]).unwrap();
27072707
let secp_ctx = Secp256k1::new();
2708+
let mut node_id_1 = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_1_key));
2709+
let mut node_id_2 = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_2_key));
2710+
let mut node_signer_1 = &node_1_key;
2711+
let mut node_signer_2 = &node_2_key;
2712+
if node_id_1 > node_id_2 {
2713+
core::mem::swap(&mut node_id_1, &mut node_id_2);
2714+
core::mem::swap(&mut node_signer_1, &mut node_signer_2);
2715+
}
27082716
let unsigned_announcement = UnsignedChannelAnnouncement {
27092717
features: channelmanager::provided_channel_features(&UserConfig::default()),
27102718
chain_hash: genesis_hash,
27112719
short_channel_id,
2712-
node_id_1: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_1_key)),
2713-
node_id_2: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_2_key)),
2720+
node_id_1,
2721+
node_id_2,
27142722
bitcoin_key_1: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_1_secret)),
27152723
bitcoin_key_2: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_2_secret)),
27162724
excess_data: Vec::new(),
27172725
};
27182726
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
27192727
let signed_announcement = ChannelAnnouncement {
2720-
node_signature_1: secp_ctx.sign_ecdsa(&msghash, &node_1_key),
2721-
node_signature_2: secp_ctx.sign_ecdsa(&msghash, &node_2_key),
2728+
node_signature_1: secp_ctx.sign_ecdsa(&msghash, node_signer_1),
2729+
node_signature_2: secp_ctx.sign_ecdsa(&msghash, node_signer_2),
27222730
bitcoin_signature_1: secp_ctx.sign_ecdsa(&msghash, &node_1_secret),
27232731
bitcoin_signature_2: secp_ctx.sign_ecdsa(&msghash, &node_2_secret),
27242732
contents: unsigned_announcement,
@@ -2732,10 +2740,23 @@ mod tests {
27322740

27332741
fn update_channel(
27342742
network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_key: SecretKey,
2735-
channel_flags: u8, htlc_maximum_msat: u64, timestamp: u32,
2743+
mut channel_flags: u8, htlc_maximum_msat: u64, timestamp: u32,
27362744
) {
27372745
let genesis_hash = ChainHash::using_genesis_block(Network::Testnet);
27382746
let secp_ctx = Secp256k1::new();
2747+
let node_id = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_key));
2748+
// `add_channel` may have swapped the node order to satisfy the spec's sorted node_ids
2749+
// requirement, so override `channel_flags` bit 0 to match the actual node position.
2750+
{
2751+
let read_only = network_graph.read_only();
2752+
if let Some(channel) = read_only.channel(short_channel_id) {
2753+
if channel.node_one == node_id {
2754+
channel_flags &= !1;
2755+
} else {
2756+
channel_flags |= 1;
2757+
}
2758+
}
2759+
}
27392760
let unsigned_update = UnsignedChannelUpdate {
27402761
chain_hash: genesis_hash,
27412762
short_channel_id,

lightning/src/routing/test_utils.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,14 @@ pub(crate) fn channel_announcement(
3636
node_1_privkey: &SecretKey, node_2_privkey: &SecretKey, features: ChannelFeatures,
3737
short_channel_id: u64, secp_ctx: &Secp256k1<All>,
3838
) -> ChannelAnnouncement {
39-
let node_id_1 = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, node_1_privkey));
40-
let node_id_2 = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, node_2_privkey));
39+
let mut node_id_1 = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, node_1_privkey));
40+
let mut node_id_2 = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, node_2_privkey));
41+
let mut signer_1 = node_1_privkey;
42+
let mut signer_2 = node_2_privkey;
43+
if node_id_1 > node_id_2 {
44+
core::mem::swap(&mut node_id_1, &mut node_id_2);
45+
core::mem::swap(&mut signer_1, &mut signer_2);
46+
}
4147

4248
let unsigned_announcement = UnsignedChannelAnnouncement {
4349
features,
@@ -52,10 +58,10 @@ pub(crate) fn channel_announcement(
5258

5359
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
5460
ChannelAnnouncement {
55-
node_signature_1: secp_ctx.sign_ecdsa(&msghash, node_1_privkey),
56-
node_signature_2: secp_ctx.sign_ecdsa(&msghash, node_2_privkey),
57-
bitcoin_signature_1: secp_ctx.sign_ecdsa(&msghash, node_1_privkey),
58-
bitcoin_signature_2: secp_ctx.sign_ecdsa(&msghash, node_2_privkey),
61+
node_signature_1: secp_ctx.sign_ecdsa(&msghash, signer_1),
62+
node_signature_2: secp_ctx.sign_ecdsa(&msghash, signer_2),
63+
bitcoin_signature_1: secp_ctx.sign_ecdsa(&msghash, signer_1),
64+
bitcoin_signature_2: secp_ctx.sign_ecdsa(&msghash, signer_2),
5965
contents: unsigned_announcement.clone(),
6066
}
6167
}
@@ -119,9 +125,25 @@ pub(crate) fn add_or_update_node(
119125

120126
pub(crate) fn update_channel(
121127
gossip_sync: &P2PGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>,
122-
secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey, update: UnsignedChannelUpdate
128+
secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey, mut update: UnsignedChannelUpdate
123129
) {
124130
let node_pubkey = PublicKey::from_secret_key(&secp_ctx, node_privkey);
131+
let node_id = NodeId::from_pubkey(&node_pubkey);
132+
133+
// `channel_announcement` may have swapped the node order to satisfy the spec's sorted node_ids
134+
// requirement, so override `channel_flags` bit 0 to match the actual node position recorded in
135+
// the network graph.
136+
{
137+
let network_graph = gossip_sync.network_graph().read_only();
138+
if let Some(channel) = network_graph.channel(update.short_channel_id) {
139+
if channel.node_one == node_id {
140+
update.channel_flags &= !1;
141+
} else {
142+
update.channel_flags |= 1;
143+
}
144+
}
145+
}
146+
125147
let msghash = hash_to_message!(&Sha256dHash::hash(&update.encode()[..])[..]);
126148
let valid_channel_update = ChannelUpdate {
127149
signature: secp_ctx.sign_ecdsa(&msghash, node_privkey),

0 commit comments

Comments
 (0)