Skip to content

Commit ed13eb6

Browse files
committed
Add tests for the new async gossip checking internal APIs
1 parent b764df8 commit ed13eb6

File tree

2 files changed

+315
-10
lines changed

2 files changed

+315
-10
lines changed

lightning/src/routing/gossip.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,7 +1904,7 @@ impl ReadOnlyNetworkGraph<'_> {
19041904
}
19051905

19061906
#[cfg(test)]
1907-
mod tests {
1907+
pub(crate) mod tests {
19081908
use crate::ln::channelmanager;
19091909
use crate::ln::chan_utils::make_funding_redeemscript;
19101910
#[cfg(feature = "std")]
@@ -1971,7 +1971,7 @@ mod tests {
19711971
assert!(!gossip_sync.should_request_full_sync(&node_id));
19721972
}
19731973

1974-
fn get_signed_node_announcement<F: Fn(&mut UnsignedNodeAnnouncement)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> NodeAnnouncement {
1974+
pub(crate) fn get_signed_node_announcement<F: Fn(&mut UnsignedNodeAnnouncement)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> NodeAnnouncement {
19751975
let node_id = PublicKey::from_secret_key(&secp_ctx, node_key);
19761976
let mut unsigned_announcement = UnsignedNodeAnnouncement {
19771977
features: channelmanager::provided_node_features(&UserConfig::default()),
@@ -1991,7 +1991,7 @@ mod tests {
19911991
}
19921992
}
19931993

1994-
fn get_signed_channel_announcement<F: Fn(&mut UnsignedChannelAnnouncement)>(f: F, node_1_key: &SecretKey, node_2_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelAnnouncement {
1994+
pub(crate) fn get_signed_channel_announcement<F: Fn(&mut UnsignedChannelAnnouncement)>(f: F, node_1_key: &SecretKey, node_2_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelAnnouncement {
19951995
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_key);
19961996
let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_key);
19971997
let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap();
@@ -2018,14 +2018,14 @@ mod tests {
20182018
}
20192019
}
20202020

2021-
fn get_channel_script(secp_ctx: &Secp256k1<secp256k1::All>) -> Script {
2021+
pub(crate) fn get_channel_script(secp_ctx: &Secp256k1<secp256k1::All>) -> Script {
20222022
let node_1_btckey = SecretKey::from_slice(&[40; 32]).unwrap();
20232023
let node_2_btckey = SecretKey::from_slice(&[39; 32]).unwrap();
20242024
make_funding_redeemscript(&PublicKey::from_secret_key(secp_ctx, &node_1_btckey),
20252025
&PublicKey::from_secret_key(secp_ctx, &node_2_btckey)).to_v0_p2wsh()
20262026
}
20272027

2028-
fn get_signed_channel_update<F: Fn(&mut UnsignedChannelUpdate)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelUpdate {
2028+
pub(crate) fn get_signed_channel_update<F: Fn(&mut UnsignedChannelUpdate)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelUpdate {
20292029
let mut unsigned_channel_update = UnsignedChannelUpdate {
20302030
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
20312031
short_channel_id: 0,

lightning/src/routing/gossip_checking.rs

Lines changed: 310 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,6 @@ impl PendingChecks {
373373
if latest_announce.is_none() ||
374374
latest_announce.as_ref().unwrap().timestamp() < msg.timestamp
375375
{
376-
// If the messages we got has a higher timestamp, just blindly
377-
// assume the signatures on the new message are correct and drop
378-
// the old message. This may cause us to end up dropping valid
379-
// `node_announcement`s if a peer is malicious, but we should get
380-
// the correct ones when the node updates them.
381376
*latest_announce = Some(
382377
if let Some(msg) = full_msg { NodeAnnouncement::Full(msg.clone()) }
383378
else { NodeAnnouncement::Unsigned(msg.clone()) });
@@ -563,3 +558,313 @@ impl PendingChecks {
563558
}
564559
}
565560
}
561+
562+
#[cfg(test)]
563+
mod tests {
564+
use super::*;
565+
use crate::routing::gossip::tests::*;
566+
use crate::util::test_utils::{TestChainSource, TestLogger};
567+
use crate::ln::msgs;
568+
569+
use bitcoin::blockdata::constants::genesis_block;
570+
use bitcoin::secp256k1::{Secp256k1, SecretKey};
571+
572+
use core::sync::atomic::Ordering;
573+
574+
fn get_network() -> (TestChainSource, NetworkGraph<Box<TestLogger>>) {
575+
let logger = Box::new(TestLogger::new());
576+
let genesis_hash = genesis_block(bitcoin::Network::Testnet).header.block_hash();
577+
let chain_source = TestChainSource::new(bitcoin::Network::Testnet);
578+
let network_graph = NetworkGraph::new(genesis_hash, logger);
579+
580+
(chain_source, network_graph)
581+
}
582+
583+
fn get_test_objects() -> (msgs::ChannelAnnouncement, TestChainSource,
584+
NetworkGraph<Box<TestLogger>>, bitcoin::Script, msgs::NodeAnnouncement,
585+
msgs::NodeAnnouncement, msgs::ChannelUpdate, msgs::ChannelUpdate, msgs::ChannelUpdate)
586+
{
587+
let secp_ctx = Secp256k1::new();
588+
589+
let (chain_source, network_graph) = get_network();
590+
591+
let good_script = get_channel_script(&secp_ctx);
592+
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
593+
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
594+
let valid_announcement = get_signed_channel_announcement(|_| {}, node_1_privkey, node_2_privkey, &secp_ctx);
595+
596+
let node_a_announce = get_signed_node_announcement(|_| {}, node_1_privkey, &secp_ctx);
597+
let node_b_announce = get_signed_node_announcement(|_| {}, node_2_privkey, &secp_ctx);
598+
599+
// Note that we have to set the "direction" flag correctly on both messages
600+
let chan_update_a = get_signed_channel_update(|msg| msg.flags = 0, node_1_privkey, &secp_ctx);
601+
let chan_update_b = get_signed_channel_update(|msg| msg.flags = 1, node_2_privkey, &secp_ctx);
602+
let chan_update_c = get_signed_channel_update(|msg| {
603+
msg.flags = 1; msg.timestamp += 1; }, node_2_privkey, &secp_ctx);
604+
605+
(valid_announcement, chain_source, network_graph, good_script, node_a_announce,
606+
node_b_announce, chan_update_a, chan_update_b, chan_update_c)
607+
}
608+
609+
#[test]
610+
fn test_fast_async_lookup() {
611+
// Check that async lookups which resolve quicker than the future is returned to the
612+
// `get_utxo` call can read it still resolve properly.
613+
let (valid_announcement, chain_source, network_graph, good_script, ..) = get_test_objects();
614+
615+
let future = AccessFuture::new();
616+
future.resolve_without_forwarding(&network_graph,
617+
Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
618+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
619+
620+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap();
621+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_some());
622+
}
623+
624+
#[test]
625+
fn test_async_lookup() {
626+
// Test a simple async lookup
627+
let (valid_announcement, chain_source, network_graph, good_script,
628+
node_a_announce, node_b_announce, ..) = get_test_objects();
629+
630+
let future = AccessFuture::new();
631+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
632+
633+
assert_eq!(
634+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err().err,
635+
"Channel being checked async");
636+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
637+
638+
future.resolve_without_forwarding(&network_graph,
639+
Ok(TxOut { value: 0, script_pubkey: good_script }));
640+
network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).unwrap();
641+
network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).unwrap();
642+
643+
assert!(network_graph.read_only().nodes()
644+
.get(&NodeId::from_pubkey(&valid_announcement.contents.node_id_1)).unwrap()
645+
.announcement_info.is_none());
646+
647+
network_graph.update_node_from_announcement(&node_a_announce).unwrap();
648+
network_graph.update_node_from_announcement(&node_b_announce).unwrap();
649+
650+
assert!(network_graph.read_only().nodes()
651+
.get(&NodeId::from_pubkey(&valid_announcement.contents.node_id_1)).unwrap()
652+
.announcement_info.is_some());
653+
}
654+
655+
#[test]
656+
fn test_invalid_async_lookup() {
657+
// Test an async lookup which returns an incorrect script
658+
let (valid_announcement, chain_source, network_graph, ..) = get_test_objects();
659+
660+
let future = AccessFuture::new();
661+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
662+
663+
assert_eq!(
664+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err().err,
665+
"Channel being checked async");
666+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
667+
668+
future.resolve_without_forwarding(&network_graph,
669+
Ok(TxOut { value: 1_000_000, script_pubkey: bitcoin::Script::new() }));
670+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
671+
}
672+
673+
#[test]
674+
fn test_failing_async_lookup() {
675+
// Test an async lookup which returns an error
676+
let (valid_announcement, chain_source, network_graph, ..) = get_test_objects();
677+
678+
let future = AccessFuture::new();
679+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
680+
681+
assert_eq!(
682+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err().err,
683+
"Channel being checked async");
684+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
685+
686+
future.resolve_without_forwarding(&network_graph, Err(ChainAccessError::UnknownTx));
687+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
688+
}
689+
690+
#[test]
691+
fn test_updates_async_lookup() {
692+
// Test async lookups will process pending channel_update/node_announcements once they
693+
// complete.
694+
let (valid_announcement, chain_source, network_graph, good_script, node_a_announce,
695+
node_b_announce, chan_update_a, chan_update_b, ..) = get_test_objects();
696+
697+
let future = AccessFuture::new();
698+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
699+
700+
assert_eq!(
701+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err().err,
702+
"Channel being checked async");
703+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
704+
705+
assert_eq!(
706+
network_graph.update_node_from_announcement(&node_a_announce).unwrap_err().err,
707+
"Awaiting channel_announcement validation to accept node_announcement");
708+
assert_eq!(
709+
network_graph.update_node_from_announcement(&node_b_announce).unwrap_err().err,
710+
"Awaiting channel_announcement validation to accept node_announcement");
711+
712+
assert_eq!(network_graph.update_channel(&chan_update_a).unwrap_err().err,
713+
"Awaiting channel_announcement validation to accept channel_update");
714+
assert_eq!(network_graph.update_channel(&chan_update_b).unwrap_err().err,
715+
"Awaiting channel_announcement validation to accept channel_update");
716+
717+
future.resolve_without_forwarding(&network_graph,
718+
Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
719+
720+
assert!(network_graph.read_only().channels()
721+
.get(&valid_announcement.contents.short_channel_id).unwrap().one_to_two.is_some());
722+
assert!(network_graph.read_only().channels()
723+
.get(&valid_announcement.contents.short_channel_id).unwrap().two_to_one.is_some());
724+
725+
assert!(network_graph.read_only().nodes()
726+
.get(&NodeId::from_pubkey(&valid_announcement.contents.node_id_1)).unwrap()
727+
.announcement_info.is_some());
728+
assert!(network_graph.read_only().nodes()
729+
.get(&NodeId::from_pubkey(&valid_announcement.contents.node_id_2)).unwrap()
730+
.announcement_info.is_some());
731+
}
732+
733+
#[test]
734+
fn test_latest_update_async_lookup() {
735+
// Test async lookups will process the latest channel_update if two are received while
736+
// awaiting an async UTXO lookup.
737+
let (valid_announcement, chain_source, network_graph, good_script, _,
738+
_, chan_update_a, chan_update_b, chan_update_c, ..) = get_test_objects();
739+
740+
let future = AccessFuture::new();
741+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
742+
743+
assert_eq!(
744+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err().err,
745+
"Channel being checked async");
746+
assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
747+
748+
assert_eq!(network_graph.update_channel(&chan_update_a).unwrap_err().err,
749+
"Awaiting channel_announcement validation to accept channel_update");
750+
assert_eq!(network_graph.update_channel(&chan_update_b).unwrap_err().err,
751+
"Awaiting channel_announcement validation to accept channel_update");
752+
assert_eq!(network_graph.update_channel(&chan_update_c).unwrap_err().err,
753+
"Awaiting channel_announcement validation to accept channel_update");
754+
755+
future.resolve_without_forwarding(&network_graph,
756+
Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
757+
758+
assert_eq!(chan_update_a.contents.timestamp, chan_update_b.contents.timestamp);
759+
assert!(network_graph.read_only().channels()
760+
.get(&valid_announcement.contents.short_channel_id).as_ref().unwrap()
761+
.one_to_two.as_ref().unwrap().last_update !=
762+
network_graph.read_only().channels()
763+
.get(&valid_announcement.contents.short_channel_id).as_ref().unwrap()
764+
.two_to_one.as_ref().unwrap().last_update);
765+
}
766+
767+
#[test]
768+
fn test_no_double_lookups() {
769+
// Test that a pending async lookup will prevent a second async lookup from flying, but
770+
// only if the channel_announcement message is identical.
771+
let (valid_announcement, chain_source, network_graph, good_script, ..) = get_test_objects();
772+
773+
let future = AccessFuture::new();
774+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
775+
776+
assert_eq!(
777+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err().err,
778+
"Channel being checked async");
779+
assert_eq!(chain_source.get_utxo_call_count.load(Ordering::Relaxed), 1);
780+
781+
// If we make a second request with the same message, the call count doesn't increase...
782+
let future_b = AccessFuture::new();
783+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future_b.clone());
784+
assert_eq!(
785+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err().err,
786+
"Channel announcement is already being checked");
787+
assert_eq!(chain_source.get_utxo_call_count.load(Ordering::Relaxed), 1);
788+
789+
// But if we make a third request with a tweaked message, we should get a second call
790+
// against our new future...
791+
let secp_ctx = Secp256k1::new();
792+
let replacement_pk_1 = &SecretKey::from_slice(&[99; 32]).unwrap();
793+
let replacement_pk_2 = &SecretKey::from_slice(&[98; 32]).unwrap();
794+
let invalid_announcement = get_signed_channel_announcement(|_| {}, replacement_pk_1, replacement_pk_2, &secp_ctx);
795+
assert_eq!(
796+
network_graph.update_channel_from_announcement(&invalid_announcement, &Some(&chain_source)).unwrap_err().err,
797+
"Channel being checked async");
798+
assert_eq!(chain_source.get_utxo_call_count.load(Ordering::Relaxed), 2);
799+
800+
// Still, if we resolve the original future, the original channel will be accepted.
801+
future.resolve_without_forwarding(&network_graph,
802+
Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
803+
assert!(!network_graph.read_only().channels()
804+
.get(&valid_announcement.contents.short_channel_id).unwrap()
805+
.announcement_message.as_ref().unwrap()
806+
.contents.features.supports_unknown_test_feature());
807+
}
808+
809+
#[test]
810+
fn test_checks_backpressure() {
811+
// Test that too_many_checks_pending returns true when there are many checks pending, and
812+
// returns false once they complete.
813+
let secp_ctx = Secp256k1::new();
814+
let (chain_source, network_graph) = get_network();
815+
816+
// We cheat and use a single future for all the lookups to complete them all at once.
817+
let future = AccessFuture::new();
818+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(future.clone());
819+
820+
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
821+
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
822+
823+
for i in 0..PendingChecks::MAX_PENDING_LOOKUPS {
824+
let valid_announcement = get_signed_channel_announcement(
825+
|msg| msg.short_channel_id += 1 + i as u64, node_1_privkey, node_2_privkey, &secp_ctx);
826+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err();
827+
assert!(!network_graph.pending_checks.too_many_checks_pending());
828+
}
829+
830+
let valid_announcement = get_signed_channel_announcement(
831+
|_| {}, node_1_privkey, node_2_privkey, &secp_ctx);
832+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err();
833+
assert!(network_graph.pending_checks.too_many_checks_pending());
834+
835+
// Once the future completes the "too many checks" flag should reset.
836+
future.resolve_without_forwarding(&network_graph, Err(ChainAccessError::UnknownTx));
837+
assert!(!network_graph.pending_checks.too_many_checks_pending());
838+
}
839+
840+
#[test]
841+
fn test_checks_backpressure_drop() {
842+
// Test that too_many_checks_pending returns true when there are many checks pending, and
843+
// returns false if we drop some of the futures without completion.
844+
let secp_ctx = Secp256k1::new();
845+
let (chain_source, network_graph) = get_network();
846+
847+
// We cheat and use a single future for all the lookups to complete them all at once.
848+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Async(AccessFuture::new());
849+
850+
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
851+
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
852+
853+
for i in 0..PendingChecks::MAX_PENDING_LOOKUPS {
854+
let valid_announcement = get_signed_channel_announcement(
855+
|msg| msg.short_channel_id += 1 + i as u64, node_1_privkey, node_2_privkey, &secp_ctx);
856+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err();
857+
assert!(!network_graph.pending_checks.too_many_checks_pending());
858+
}
859+
860+
let valid_announcement = get_signed_channel_announcement(
861+
|_| {}, node_1_privkey, node_2_privkey, &secp_ctx);
862+
network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap_err();
863+
assert!(network_graph.pending_checks.too_many_checks_pending());
864+
865+
// Once the future is drop'd (by resetting the `utxo_ret` value) the "too many checks" flag
866+
// should reset to false.
867+
*chain_source.utxo_ret.lock().unwrap() = ChainAccessResult::Sync(Err(ChainAccessError::UnknownTx));
868+
assert!(!network_graph.pending_checks.too_many_checks_pending());
869+
}
870+
}

0 commit comments

Comments
 (0)