@@ -286,6 +286,11 @@ impl Refund {
286286 pub fn payer_note ( & self ) -> Option < PrintableString > {
287287 self . contents . payer_note . as_ref ( ) . map ( |payer_note| PrintableString ( payer_note. as_str ( ) ) )
288288 }
289+
290+ #[ cfg( test) ]
291+ fn as_tlv_stream ( & self ) -> RefundTlvStreamRef {
292+ self . contents . as_tlv_stream ( )
293+ }
289294}
290295
291296impl AsRef < [ u8 ] > for Refund {
@@ -472,3 +477,227 @@ impl core::fmt::Display for Refund {
472477 self . fmt_bech32_str ( f)
473478 }
474479}
480+
481+ #[ cfg( test) ]
482+ mod tests {
483+ use super :: { Refund , RefundBuilder } ;
484+
485+ use bitcoin:: blockdata:: constants:: ChainHash ;
486+ use bitcoin:: network:: constants:: Network ;
487+ use bitcoin:: secp256k1:: { KeyPair , PublicKey , Secp256k1 , SecretKey } ;
488+ use core:: convert:: TryFrom ;
489+ use core:: time:: Duration ;
490+ use crate :: ln:: features:: InvoiceRequestFeatures ;
491+ use crate :: ln:: msgs:: MAX_VALUE_MSAT ;
492+ use crate :: offers:: invoice_request:: InvoiceRequestTlvStreamRef ;
493+ use crate :: offers:: offer:: OfferTlvStreamRef ;
494+ use crate :: offers:: parse:: SemanticError ;
495+ use crate :: offers:: payer:: PayerTlvStreamRef ;
496+ use crate :: onion_message:: { BlindedHop , BlindedPath } ;
497+ use crate :: util:: ser:: Writeable ;
498+ use crate :: util:: string:: PrintableString ;
499+
500+ fn payer_pubkey ( ) -> PublicKey {
501+ let secp_ctx = Secp256k1 :: new ( ) ;
502+ KeyPair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) . public_key ( )
503+ }
504+
505+ fn pubkey ( byte : u8 ) -> PublicKey {
506+ let secp_ctx = Secp256k1 :: new ( ) ;
507+ PublicKey :: from_secret_key ( & secp_ctx, & privkey ( byte) )
508+ }
509+
510+ fn privkey ( byte : u8 ) -> SecretKey {
511+ SecretKey :: from_slice ( & [ byte; 32 ] ) . unwrap ( )
512+ }
513+
514+ #[ test]
515+ fn builds_refund_with_defaults ( ) {
516+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
517+ . build ( ) . unwrap ( ) ;
518+
519+ let mut buffer = Vec :: new ( ) ;
520+ refund. write ( & mut buffer) . unwrap ( ) ;
521+
522+ assert_eq ! ( refund. bytes, buffer. as_slice( ) ) ;
523+ assert_eq ! ( refund. metadata( ) , & [ 1 ; 32 ] ) ;
524+ assert_eq ! ( refund. description( ) , PrintableString ( "foo" ) ) ;
525+ assert_eq ! ( refund. absolute_expiry( ) , None ) ;
526+ #[ cfg( feature = "std" ) ]
527+ assert ! ( !refund. is_expired( ) ) ;
528+ assert_eq ! ( refund. paths( ) , & [ ] ) ;
529+ assert_eq ! ( refund. issuer( ) , None ) ;
530+ assert_eq ! ( refund. chain( ) , ChainHash :: using_genesis_block( Network :: Bitcoin ) ) ;
531+ assert_eq ! ( refund. amount_msats( ) , 1000 ) ;
532+ assert_eq ! ( refund. features( ) , & InvoiceRequestFeatures :: empty( ) ) ;
533+ assert_eq ! ( refund. payer_id( ) , payer_pubkey( ) ) ;
534+ assert_eq ! ( refund. payer_note( ) , None ) ;
535+
536+ assert_eq ! (
537+ refund. as_tlv_stream( ) ,
538+ (
539+ PayerTlvStreamRef { metadata: Some ( & vec![ 1 ; 32 ] ) } ,
540+ OfferTlvStreamRef {
541+ chains: None ,
542+ metadata: None ,
543+ currency: None ,
544+ amount: None ,
545+ description: Some ( & String :: from( "foo" ) ) ,
546+ features: None ,
547+ absolute_expiry: None ,
548+ paths: None ,
549+ issuer: None ,
550+ quantity_max: None ,
551+ node_id: None ,
552+ } ,
553+ InvoiceRequestTlvStreamRef {
554+ chain: None ,
555+ amount: Some ( 1000 ) ,
556+ features: None ,
557+ quantity: None ,
558+ payer_id: Some ( & payer_pubkey( ) ) ,
559+ payer_note: None ,
560+ } ,
561+ ) ,
562+ ) ;
563+
564+ if let Err ( e) = Refund :: try_from ( buffer) {
565+ panic ! ( "error parsing refund: {:?}" , e) ;
566+ }
567+ }
568+
569+ #[ test]
570+ fn fails_building_refund_with_invalid_amount ( ) {
571+ match RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , MAX_VALUE_MSAT + 1 ) {
572+ Ok ( _) => panic ! ( "expected error" ) ,
573+ Err ( e) => assert_eq ! ( e, SemanticError :: InvalidAmount ) ,
574+ }
575+ }
576+
577+ #[ test]
578+ fn builds_refund_with_absolute_expiry ( ) {
579+ let future_expiry = Duration :: from_secs ( u64:: max_value ( ) ) ;
580+ let past_expiry = Duration :: from_secs ( 0 ) ;
581+
582+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
583+ . absolute_expiry ( future_expiry)
584+ . build ( )
585+ . unwrap ( ) ;
586+ let ( _, tlv_stream, _) = refund. as_tlv_stream ( ) ;
587+ #[ cfg( feature = "std" ) ]
588+ assert ! ( !refund. is_expired( ) ) ;
589+ assert_eq ! ( refund. absolute_expiry( ) , Some ( future_expiry) ) ;
590+ assert_eq ! ( tlv_stream. absolute_expiry, Some ( future_expiry. as_secs( ) ) ) ;
591+
592+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
593+ . absolute_expiry ( future_expiry)
594+ . absolute_expiry ( past_expiry)
595+ . build ( )
596+ . unwrap ( ) ;
597+ let ( _, tlv_stream, _) = refund. as_tlv_stream ( ) ;
598+ #[ cfg( feature = "std" ) ]
599+ assert ! ( refund. is_expired( ) ) ;
600+ assert_eq ! ( refund. absolute_expiry( ) , Some ( past_expiry) ) ;
601+ assert_eq ! ( tlv_stream. absolute_expiry, Some ( past_expiry. as_secs( ) ) ) ;
602+ }
603+
604+ #[ test]
605+ fn builds_refund_with_paths ( ) {
606+ let paths = vec ! [
607+ BlindedPath {
608+ introduction_node_id: pubkey( 40 ) ,
609+ blinding_point: pubkey( 41 ) ,
610+ blinded_hops: vec![
611+ BlindedHop { blinded_node_id: pubkey( 43 ) , encrypted_payload: vec![ 0 ; 43 ] } ,
612+ BlindedHop { blinded_node_id: pubkey( 44 ) , encrypted_payload: vec![ 0 ; 44 ] } ,
613+ ] ,
614+ } ,
615+ BlindedPath {
616+ introduction_node_id: pubkey( 40 ) ,
617+ blinding_point: pubkey( 41 ) ,
618+ blinded_hops: vec![
619+ BlindedHop { blinded_node_id: pubkey( 45 ) , encrypted_payload: vec![ 0 ; 45 ] } ,
620+ BlindedHop { blinded_node_id: pubkey( 46 ) , encrypted_payload: vec![ 0 ; 46 ] } ,
621+ ] ,
622+ } ,
623+ ] ;
624+
625+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
626+ . path ( paths[ 0 ] . clone ( ) )
627+ . path ( paths[ 1 ] . clone ( ) )
628+ . build ( )
629+ . unwrap ( ) ;
630+ let ( _, offer_tlv_stream, invoice_request_tlv_stream) = refund. as_tlv_stream ( ) ;
631+ assert_eq ! ( refund. paths( ) , paths. as_slice( ) ) ;
632+ assert_eq ! ( refund. payer_id( ) , pubkey( 42 ) ) ;
633+ assert_ne ! ( pubkey( 42 ) , pubkey( 44 ) ) ;
634+ assert_eq ! ( offer_tlv_stream. paths, Some ( & paths) ) ;
635+ assert_eq ! ( invoice_request_tlv_stream. payer_id, Some ( & pubkey( 42 ) ) ) ;
636+ }
637+
638+ #[ test]
639+ fn builds_refund_with_issuer ( ) {
640+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
641+ . issuer ( "bar" . into ( ) )
642+ . build ( )
643+ . unwrap ( ) ;
644+ let ( _, tlv_stream, _) = refund. as_tlv_stream ( ) ;
645+ assert_eq ! ( refund. issuer( ) , Some ( PrintableString ( "bar" ) ) ) ;
646+ assert_eq ! ( tlv_stream. issuer, Some ( & String :: from( "bar" ) ) ) ;
647+
648+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
649+ . issuer ( "bar" . into ( ) )
650+ . issuer ( "baz" . into ( ) )
651+ . build ( )
652+ . unwrap ( ) ;
653+ let ( _, tlv_stream, _) = refund. as_tlv_stream ( ) ;
654+ assert_eq ! ( refund. issuer( ) , Some ( PrintableString ( "baz" ) ) ) ;
655+ assert_eq ! ( tlv_stream. issuer, Some ( & String :: from( "baz" ) ) ) ;
656+ }
657+
658+ #[ test]
659+ fn builds_refund_with_chain ( ) {
660+ let mainnet = ChainHash :: using_genesis_block ( Network :: Bitcoin ) ;
661+ let testnet = ChainHash :: using_genesis_block ( Network :: Testnet ) ;
662+
663+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
664+ . chain ( Network :: Bitcoin )
665+ . build ( ) . unwrap ( ) ;
666+ let ( _, _, tlv_stream) = refund. as_tlv_stream ( ) ;
667+ assert_eq ! ( refund. chain( ) , mainnet) ;
668+ assert_eq ! ( tlv_stream. chain, None ) ;
669+
670+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
671+ . chain ( Network :: Testnet )
672+ . build ( ) . unwrap ( ) ;
673+ let ( _, _, tlv_stream) = refund. as_tlv_stream ( ) ;
674+ assert_eq ! ( refund. chain( ) , testnet) ;
675+ assert_eq ! ( tlv_stream. chain, Some ( & testnet) ) ;
676+
677+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
678+ . chain ( Network :: Regtest )
679+ . chain ( Network :: Testnet )
680+ . build ( ) . unwrap ( ) ;
681+ let ( _, _, tlv_stream) = refund. as_tlv_stream ( ) ;
682+ assert_eq ! ( refund. chain( ) , testnet) ;
683+ assert_eq ! ( tlv_stream. chain, Some ( & testnet) ) ;
684+ }
685+
686+ #[ test]
687+ fn builds_refund_with_payer_note ( ) {
688+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
689+ . payer_note ( "bar" . into ( ) )
690+ . build ( ) . unwrap ( ) ;
691+ let ( _, _, tlv_stream) = refund. as_tlv_stream ( ) ;
692+ assert_eq ! ( refund. payer_note( ) , Some ( PrintableString ( "bar" ) ) ) ;
693+ assert_eq ! ( tlv_stream. payer_note, Some ( & String :: from( "bar" ) ) ) ;
694+
695+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
696+ . payer_note ( "bar" . into ( ) )
697+ . payer_note ( "baz" . into ( ) )
698+ . build ( ) . unwrap ( ) ;
699+ let ( _, _, tlv_stream) = refund. as_tlv_stream ( ) ;
700+ assert_eq ! ( refund. payer_note( ) , Some ( PrintableString ( "baz" ) ) ) ;
701+ assert_eq ! ( tlv_stream. payer_note, Some ( & String :: from( "baz" ) ) ) ;
702+ }
703+ }
0 commit comments