Skip to content

Commit 89c5356

Browse files
committed
Add tests for the new async gossip checking internal APIs
1 parent 710f6c0 commit 89c5356

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

0 commit comments

Comments
 (0)