Skip to content

Commit 0ecd101

Browse files
committed
Add tests for the new async gossip checking internal APIs
1 parent 66b315a commit 0ecd101

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

0 commit comments

Comments
 (0)