@@ -286,6 +286,11 @@ impl Refund {
286
286
pub fn payer_note ( & self ) -> Option < PrintableString > {
287
287
self . contents . payer_note . as_ref ( ) . map ( |payer_note| PrintableString ( payer_note. as_str ( ) ) )
288
288
}
289
+
290
+ #[ cfg( test) ]
291
+ fn as_tlv_stream ( & self ) -> RefundTlvStreamRef {
292
+ self . contents . as_tlv_stream ( )
293
+ }
289
294
}
290
295
291
296
impl AsRef < [ u8 ] > for Refund {
@@ -472,3 +477,227 @@ impl core::fmt::Display for Refund {
472
477
self . fmt_bech32_str ( f)
473
478
}
474
479
}
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