Skip to content

Commit 17ae116

Browse files
authored
Merge pull request #1679 from TheBlueMatt/2022-08-no-double-access
Avoid querying the chain for outputs for channels we already have
2 parents 187d18a + 5847abd commit 17ae116

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)