Skip to content

Commit 52950dc

Browse files
committed
Add tests for the new async gossip checking internal APIs
1 parent ee93563 commit 52950dc

File tree

2 files changed

+315
-5
lines changed

2 files changed

+315
-5
lines changed

lightning/src/routing/gossip.rs

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

19521952
#[cfg(test)]
1953-
mod tests {
1953+
pub(crate) mod tests {
19541954
use crate::ln::channelmanager;
19551955
use crate::ln::chan_utils::make_funding_redeemscript;
19561956
use crate::ln::features::InitFeatures;
@@ -2016,7 +2016,7 @@ mod tests {
20162016
assert!(!gossip_sync.should_request_full_sync(&node_id));
20172017
}
20182018

2019-
fn get_signed_node_announcement<F: Fn(&mut UnsignedNodeAnnouncement)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> NodeAnnouncement {
2019+
pub(crate) fn get_signed_node_announcement<F: Fn(&mut UnsignedNodeAnnouncement)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> NodeAnnouncement {
20202020
let node_id = PublicKey::from_secret_key(&secp_ctx, node_key);
20212021
let mut unsigned_announcement = UnsignedNodeAnnouncement {
20222022
features: channelmanager::provided_node_features(&UserConfig::default()),
@@ -2036,7 +2036,7 @@ mod tests {
20362036
}
20372037
}
20382038

2039-
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 {
2039+
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 {
20402040
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_key);
20412041
let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_key);
20422042
let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap();
@@ -2063,14 +2063,14 @@ mod tests {
20632063
}
20642064
}
20652065

2066-
fn get_channel_script(secp_ctx: &Secp256k1<secp256k1::All>) -> Script {
2066+
pub(crate) fn get_channel_script(secp_ctx: &Secp256k1<secp256k1::All>) -> Script {
20672067
let node_1_btckey = SecretKey::from_slice(&[40; 32]).unwrap();
20682068
let node_2_btckey = SecretKey::from_slice(&[39; 32]).unwrap();
20692069
make_funding_redeemscript(&PublicKey::from_secret_key(secp_ctx, &node_1_btckey),
20702070
&PublicKey::from_secret_key(secp_ctx, &node_2_btckey)).to_v0_p2wsh()
20712071
}
20722072

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

lightning/src/routing/gossip_checking.rs

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

0 commit comments

Comments
 (0)