@@ -183,6 +183,14 @@ impl RefundBuilder {
183
183
}
184
184
}
185
185
186
+ #[ cfg( test) ]
187
+ impl RefundBuilder {
188
+ fn features_unchecked ( mut self , features : InvoiceRequestFeatures ) -> Self {
189
+ self . refund . features = features;
190
+ self
191
+ }
192
+ }
193
+
186
194
/// A `Refund` is a request to send an `Invoice` without a preceding [`Offer`].
187
195
///
188
196
/// Typically, after an invoice is paid, the recipient may publish a refund allowing the sender to
@@ -480,21 +488,21 @@ impl core::fmt::Display for Refund {
480
488
481
489
#[ cfg( test) ]
482
490
mod tests {
483
- use super :: { Refund , RefundBuilder } ;
491
+ use super :: { Refund , RefundBuilder , RefundTlvStreamRef } ;
484
492
485
493
use bitcoin:: blockdata:: constants:: ChainHash ;
486
494
use bitcoin:: network:: constants:: Network ;
487
495
use bitcoin:: secp256k1:: { KeyPair , PublicKey , Secp256k1 , SecretKey } ;
488
496
use core:: convert:: TryFrom ;
489
497
use core:: time:: Duration ;
490
- use crate :: ln:: features:: InvoiceRequestFeatures ;
491
- use crate :: ln:: msgs:: MAX_VALUE_MSAT ;
498
+ use crate :: ln:: features:: { InvoiceRequestFeatures , OfferFeatures } ;
499
+ use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
492
500
use crate :: offers:: invoice_request:: InvoiceRequestTlvStreamRef ;
493
501
use crate :: offers:: offer:: OfferTlvStreamRef ;
494
- use crate :: offers:: parse:: SemanticError ;
502
+ use crate :: offers:: parse:: { ParseError , SemanticError } ;
495
503
use crate :: offers:: payer:: PayerTlvStreamRef ;
496
504
use crate :: onion_message:: { BlindedHop , BlindedPath } ;
497
- use crate :: util:: ser:: Writeable ;
505
+ use crate :: util:: ser:: { BigSize , Writeable } ;
498
506
use crate :: util:: string:: PrintableString ;
499
507
500
508
fn payer_pubkey ( ) -> PublicKey {
@@ -511,6 +519,18 @@ mod tests {
511
519
SecretKey :: from_slice ( & [ byte; 32 ] ) . unwrap ( )
512
520
}
513
521
522
+ trait ToBytes {
523
+ fn to_bytes ( & self ) -> Vec < u8 > ;
524
+ }
525
+
526
+ impl < ' a > ToBytes for RefundTlvStreamRef < ' a > {
527
+ fn to_bytes ( & self ) -> Vec < u8 > {
528
+ let mut buffer = Vec :: new ( ) ;
529
+ self . write ( & mut buffer) . unwrap ( ) ;
530
+ buffer
531
+ }
532
+ }
533
+
514
534
#[ test]
515
535
fn builds_refund_with_defaults ( ) {
516
536
let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
@@ -700,4 +720,229 @@ mod tests {
700
720
assert_eq ! ( refund. payer_note( ) , Some ( PrintableString ( "baz" ) ) ) ;
701
721
assert_eq ! ( tlv_stream. payer_note, Some ( & String :: from( "baz" ) ) ) ;
702
722
}
723
+
724
+ #[ test]
725
+ fn parses_refund_with_metadata ( ) {
726
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
727
+ . build ( ) . unwrap ( ) ;
728
+ if let Err ( e) = refund. to_string ( ) . parse :: < Refund > ( ) {
729
+ panic ! ( "error parsing refund: {:?}" , e) ;
730
+ }
731
+
732
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
733
+ tlv_stream. 0 . metadata = None ;
734
+
735
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
736
+ Ok ( _) => panic ! ( "expected error" ) ,
737
+ Err ( e) => {
738
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: MissingPayerMetadata ) ) ;
739
+ } ,
740
+ }
741
+ }
742
+
743
+ #[ test]
744
+ fn parses_refund_with_description ( ) {
745
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
746
+ . build ( ) . unwrap ( ) ;
747
+ if let Err ( e) = refund. to_string ( ) . parse :: < Refund > ( ) {
748
+ panic ! ( "error parsing refund: {:?}" , e) ;
749
+ }
750
+
751
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
752
+ tlv_stream. 1 . description = None ;
753
+
754
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
755
+ Ok ( _) => panic ! ( "expected error" ) ,
756
+ Err ( e) => {
757
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: MissingDescription ) ) ;
758
+ } ,
759
+ }
760
+ }
761
+
762
+ #[ test]
763
+ fn parses_refund_with_amount ( ) {
764
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
765
+ . build ( ) . unwrap ( ) ;
766
+ if let Err ( e) = refund. to_string ( ) . parse :: < Refund > ( ) {
767
+ panic ! ( "error parsing refund: {:?}" , e) ;
768
+ }
769
+
770
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
771
+ tlv_stream. 2 . amount = None ;
772
+
773
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
774
+ Ok ( _) => panic ! ( "expected error" ) ,
775
+ Err ( e) => {
776
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: MissingAmount ) ) ;
777
+ } ,
778
+ }
779
+
780
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
781
+ tlv_stream. 2 . amount = Some ( MAX_VALUE_MSAT + 1 ) ;
782
+
783
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
784
+ Ok ( _) => panic ! ( "expected error" ) ,
785
+ Err ( e) => {
786
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: InvalidAmount ) ) ;
787
+ } ,
788
+ }
789
+ }
790
+
791
+ #[ test]
792
+ fn parses_refund_with_payer_id ( ) {
793
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
794
+ . build ( ) . unwrap ( ) ;
795
+ if let Err ( e) = refund. to_string ( ) . parse :: < Refund > ( ) {
796
+ panic ! ( "error parsing refund: {:?}" , e) ;
797
+ }
798
+
799
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
800
+ tlv_stream. 2 . payer_id = None ;
801
+
802
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
803
+ Ok ( _) => panic ! ( "expected error" ) ,
804
+ Err ( e) => {
805
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: MissingPayerId ) ) ;
806
+ } ,
807
+ }
808
+ }
809
+
810
+ #[ test]
811
+ fn parses_refund_with_optional_fields ( ) {
812
+ let past_expiry = Duration :: from_secs ( 0 ) ;
813
+ let paths = vec ! [
814
+ BlindedPath {
815
+ introduction_node_id: pubkey( 40 ) ,
816
+ blinding_point: pubkey( 41 ) ,
817
+ blinded_hops: vec![
818
+ BlindedHop { blinded_node_id: pubkey( 43 ) , encrypted_payload: vec![ 0 ; 43 ] } ,
819
+ BlindedHop { blinded_node_id: pubkey( 44 ) , encrypted_payload: vec![ 0 ; 44 ] } ,
820
+ ] ,
821
+ } ,
822
+ BlindedPath {
823
+ introduction_node_id: pubkey( 40 ) ,
824
+ blinding_point: pubkey( 41 ) ,
825
+ blinded_hops: vec![
826
+ BlindedHop { blinded_node_id: pubkey( 45 ) , encrypted_payload: vec![ 0 ; 45 ] } ,
827
+ BlindedHop { blinded_node_id: pubkey( 46 ) , encrypted_payload: vec![ 0 ; 46 ] } ,
828
+ ] ,
829
+ } ,
830
+ ] ;
831
+
832
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
833
+ . absolute_expiry ( past_expiry)
834
+ . issuer ( "bar" . into ( ) )
835
+ . path ( paths[ 0 ] . clone ( ) )
836
+ . path ( paths[ 1 ] . clone ( ) )
837
+ . chain ( Network :: Testnet )
838
+ . features_unchecked ( InvoiceRequestFeatures :: unknown ( ) )
839
+ . payer_note ( "baz" . into ( ) )
840
+ . build ( )
841
+ . unwrap ( ) ;
842
+ match refund. to_string ( ) . parse :: < Refund > ( ) {
843
+ Ok ( refund) => {
844
+ assert_eq ! ( refund. absolute_expiry( ) , Some ( past_expiry) ) ;
845
+ #[ cfg( feature = "std" ) ]
846
+ assert ! ( refund. is_expired( ) ) ;
847
+ assert_eq ! ( refund. paths( ) , & paths[ ..] ) ;
848
+ assert_eq ! ( refund. issuer( ) , Some ( PrintableString ( "bar" ) ) ) ;
849
+ assert_eq ! ( refund. chain( ) , ChainHash :: using_genesis_block( Network :: Testnet ) ) ;
850
+ assert_eq ! ( refund. features( ) , & InvoiceRequestFeatures :: unknown( ) ) ;
851
+ assert_eq ! ( refund. payer_note( ) , Some ( PrintableString ( "baz" ) ) ) ;
852
+ } ,
853
+ Err ( e) => panic ! ( "error parsing refund: {:?}" , e) ,
854
+ }
855
+ }
856
+
857
+ #[ test]
858
+ fn fails_parsing_refund_with_unexpected_fields ( ) {
859
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
860
+ . build ( ) . unwrap ( ) ;
861
+ if let Err ( e) = refund. to_string ( ) . parse :: < Refund > ( ) {
862
+ panic ! ( "error parsing refund: {:?}" , e) ;
863
+ }
864
+
865
+ let chains = vec ! [ ChainHash :: using_genesis_block( Network :: Testnet ) ] ;
866
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
867
+ tlv_stream. 1 . chains = Some ( & chains) ;
868
+
869
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
870
+ Ok ( _) => panic ! ( "expected error" ) ,
871
+ Err ( e) => {
872
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: UnexpectedChain ) ) ;
873
+ } ,
874
+ }
875
+
876
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
877
+ tlv_stream. 1 . currency = Some ( & b"USD" ) ;
878
+ tlv_stream. 1 . amount = Some ( 1000 ) ;
879
+
880
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
881
+ Ok ( _) => panic ! ( "expected error" ) ,
882
+ Err ( e) => {
883
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: UnexpectedAmount ) ) ;
884
+ } ,
885
+ }
886
+
887
+ let features = OfferFeatures :: unknown ( ) ;
888
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
889
+ tlv_stream. 1 . features = Some ( & features) ;
890
+
891
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
892
+ Ok ( _) => panic ! ( "expected error" ) ,
893
+ Err ( e) => {
894
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: UnexpectedFeatures ) ) ;
895
+ } ,
896
+ }
897
+
898
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
899
+ tlv_stream. 1 . quantity_max = Some ( 10 ) ;
900
+
901
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
902
+ Ok ( _) => panic ! ( "expected error" ) ,
903
+ Err ( e) => {
904
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: UnexpectedQuantity ) ) ;
905
+ } ,
906
+ }
907
+
908
+ let node_id = payer_pubkey ( ) ;
909
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
910
+ tlv_stream. 1 . node_id = Some ( & node_id) ;
911
+
912
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
913
+ Ok ( _) => panic ! ( "expected error" ) ,
914
+ Err ( e) => {
915
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: UnexpectedSigningPubkey ) ) ;
916
+ } ,
917
+ }
918
+
919
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
920
+ tlv_stream. 2 . quantity = Some ( 10 ) ;
921
+
922
+ match Refund :: try_from ( tlv_stream. to_bytes ( ) ) {
923
+ Ok ( _) => panic ! ( "expected error" ) ,
924
+ Err ( e) => {
925
+ assert_eq ! ( e, ParseError :: InvalidSemantics ( SemanticError :: UnexpectedQuantity ) ) ;
926
+ } ,
927
+ }
928
+ }
929
+
930
+ #[ test]
931
+ fn fails_parsing_refund_with_extra_tlv_records ( ) {
932
+ let secp_ctx = Secp256k1 :: new ( ) ;
933
+ let keys = KeyPair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
934
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , keys. public_key ( ) , 1000 ) . unwrap ( )
935
+ . build ( ) . unwrap ( ) ;
936
+
937
+ let mut encoded_refund = Vec :: new ( ) ;
938
+ refund. write ( & mut encoded_refund) . unwrap ( ) ;
939
+ BigSize ( 1002 ) . write ( & mut encoded_refund) . unwrap ( ) ;
940
+ BigSize ( 32 ) . write ( & mut encoded_refund) . unwrap ( ) ;
941
+ [ 42u8 ; 32 ] . write ( & mut encoded_refund) . unwrap ( ) ;
942
+
943
+ match Refund :: try_from ( encoded_refund) {
944
+ Ok ( _) => panic ! ( "expected error" ) ,
945
+ Err ( e) => assert_eq ! ( e, ParseError :: Decode ( DecodeError :: InvalidValue ) ) ,
946
+ }
947
+ }
703
948
}
0 commit comments