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