Skip to content

Commit ea6fc91

Browse files
committed
Add tests for the new async gossip checking internal APIs
1 parent 4607a32 commit ea6fc91

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

0 commit comments

Comments
 (0)