Skip to content

Commit 19f28c0

Browse files
committed
Add tests for the new async gossip checking internal APIs
1 parent affef24 commit 19f28c0

File tree

2 files changed

+311
-10
lines changed

2 files changed

+311
-10
lines changed

lightning/src/routing/gossip.rs

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

19241924
#[cfg(test)]
1925-
mod tests {
1925+
pub(crate) mod tests {
19261926
use crate::ln::channelmanager;
19271927
use crate::ln::chan_utils::make_funding_redeemscript;
19281928
#[cfg(feature = "std")]
@@ -1989,7 +1989,7 @@ mod tests {
19891989
assert!(!gossip_sync.should_request_full_sync(&node_id));
19901990
}
19911991

1992-
fn get_signed_node_announcement<F: Fn(&mut UnsignedNodeAnnouncement)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> NodeAnnouncement {
1992+
pub(crate) fn get_signed_node_announcement<F: Fn(&mut UnsignedNodeAnnouncement)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> NodeAnnouncement {
19931993
let node_id = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, node_key));
19941994
let mut unsigned_announcement = UnsignedNodeAnnouncement {
19951995
features: channelmanager::provided_node_features(&UserConfig::default()),
@@ -2009,7 +2009,7 @@ mod tests {
20092009
}
20102010
}
20112011

2012-
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 {
2012+
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 {
20132013
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_key);
20142014
let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_key);
20152015
let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap();
@@ -2036,14 +2036,14 @@ mod tests {
20362036
}
20372037
}
20382038

2039-
fn get_channel_script(secp_ctx: &Secp256k1<secp256k1::All>) -> Script {
2039+
pub(crate) fn get_channel_script(secp_ctx: &Secp256k1<secp256k1::All>) -> Script {
20402040
let node_1_btckey = SecretKey::from_slice(&[40; 32]).unwrap();
20412041
let node_2_btckey = SecretKey::from_slice(&[39; 32]).unwrap();
20422042
make_funding_redeemscript(&PublicKey::from_secret_key(secp_ctx, &node_1_btckey),
20432043
&PublicKey::from_secret_key(secp_ctx, &node_2_btckey)).to_v0_p2wsh()
20442044
}
20452045

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

lightning/src/routing/gossip_checking.rs

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

0 commit comments

Comments
 (0)