@@ -528,3 +528,313 @@ impl PendingChecks {
528
528
}
529
529
}
530
530
}
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