Skip to content

Commit 1f07cb6

Browse files
committed
Add tests for the new async gossip checking internal APIs
1 parent 0b3cf22 commit 1f07cb6

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

0 commit comments

Comments
 (0)