Skip to content

Commit 5847abd

Browse files
committed
Avoid querying the chain for outputs for channels we already have
If we receive a ChannelAnnouncement message but we already have the channel, there's no reason to do a chain lookup. Instead of immediately calling the user-provided `chain::Access` when handling a ChannelAnnouncement, we first check if we have the corresponding channel in the graph. Note that if we do have the corresponding channel but it was not previously checked against the blockchain, we should still check with the `chain::Access` and update if necessary.
1 parent 19536c6 commit 5847abd

File tree

1 file changed

+38
-24
lines changed

1 file changed

+38
-24
lines changed

lightning/src/routing/gossip.rs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,39 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
14331433
return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
14341434
}
14351435

1436+
{
1437+
let channels = self.channels.read().unwrap();
1438+
1439+
if let Some(chan) = channels.get(&msg.short_channel_id) {
1440+
if chan.capacity_sats.is_some() {
1441+
// If we'd previously looked up the channel on-chain and checked the script
1442+
// against what appears on-chain, ignore the duplicate announcement.
1443+
//
1444+
// Because a reorg could replace one channel with another at the same SCID, if
1445+
// the channel appears to be different, we re-validate. This doesn't expose us
1446+
// to any more DoS risk than not, as a peer can always flood us with
1447+
// randomly-generated SCID values anyway.
1448+
//
1449+
// We use the Node IDs rather than the bitcoin_keys to check for "equivalence"
1450+
// as we didn't (necessarily) store the bitcoin keys, and we only really care
1451+
// if the peers on the channel changed anyway.
1452+
if NodeId::from_pubkey(&msg.node_id_1) == chan.node_one && NodeId::from_pubkey(&msg.node_id_2) == chan.node_two {
1453+
return Err(LightningError {
1454+
err: "Already have chain-validated channel".to_owned(),
1455+
action: ErrorAction::IgnoreDuplicateGossip
1456+
});
1457+
}
1458+
} else if chain_access.is_none() {
1459+
// Similarly, if we can't check the chain right now anyway, ignore the
1460+
// duplicate announcement without bothering to take the channels write lock.
1461+
return Err(LightningError {
1462+
err: "Already have non-chain-validated channel".to_owned(),
1463+
action: ErrorAction::IgnoreDuplicateGossip
1464+
});
1465+
}
1466+
}
1467+
}
1468+
14361469
let utxo_value = match &chain_access {
14371470
&None => {
14381471
// Tentatively accept, potentially exposing us to DoS attacks
@@ -2046,7 +2079,7 @@ mod tests {
20462079
// drop new one on the floor, since we can't see any changes.
20472080
match gossip_sync.handle_channel_announcement(&valid_announcement) {
20482081
Ok(_) => panic!(),
2049-
Err(e) => assert_eq!(e.err, "Already have knowledge of channel")
2082+
Err(e) => assert_eq!(e.err, "Already have non-chain-validated channel")
20502083
};
20512084

20522085
// Test if an associated transaction were not on-chain (or not confirmed).
@@ -2080,32 +2113,13 @@ mod tests {
20802113
};
20812114
}
20822115

2083-
// If we receive announcement for the same channel (but TX is not confirmed),
2084-
// drop new one on the floor, since we can't see any changes.
2085-
*chain_source.utxo_ret.lock().unwrap() = Err(chain::AccessError::UnknownTx);
2086-
match gossip_sync.handle_channel_announcement(&valid_announcement) {
2087-
Ok(_) => panic!(),
2088-
Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry")
2089-
};
2090-
2091-
// But if it is confirmed, replace the channel
2116+
// If we receive announcement for the same channel, once we've validated it against the
2117+
// chain, we simply ignore all new (duplicate) announcements.
20922118
*chain_source.utxo_ret.lock().unwrap() = Ok(TxOut { value: 0, script_pubkey: good_script });
2093-
let valid_announcement = get_signed_channel_announcement(|unsigned_announcement| {
2094-
unsigned_announcement.features = ChannelFeatures::empty();
2095-
unsigned_announcement.short_channel_id += 2;
2096-
}, node_1_privkey, node_2_privkey, &secp_ctx);
20972119
match gossip_sync.handle_channel_announcement(&valid_announcement) {
2098-
Ok(res) => assert!(res),
2099-
_ => panic!()
2120+
Ok(_) => panic!(),
2121+
Err(e) => assert_eq!(e.err, "Already have chain-validated channel")
21002122
};
2101-
{
2102-
match network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id) {
2103-
Some(channel_entry) => {
2104-
assert_eq!(channel_entry.features, ChannelFeatures::empty());
2105-
},
2106-
_ => panic!()
2107-
};
2108-
}
21092123

21102124
// Don't relay valid channels with excess data
21112125
let valid_announcement = get_signed_channel_announcement(|unsigned_announcement| {

0 commit comments

Comments
 (0)