@@ -168,7 +168,7 @@ pub fn check_platform() {
168
168
///
169
169
/// (C-not exported) as we likely need to manually select one set of boolean type parameters.
170
170
#[ derive( Eq , PartialEq , Debug , Clone ) ]
171
- pub struct InvoiceBuilder < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool , C : tb:: Bool > {
171
+ pub struct InvoiceBuilder < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool , C : tb:: Bool , S : tb :: Bool > {
172
172
currency : Currency ,
173
173
amount : Option < u64 > ,
174
174
si_prefix : Option < SiPrefix > ,
@@ -180,6 +180,7 @@ pub struct InvoiceBuilder<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> {
180
180
phantom_h : std:: marker:: PhantomData < H > ,
181
181
phantom_t : std:: marker:: PhantomData < T > ,
182
182
phantom_c : std:: marker:: PhantomData < C > ,
183
+ phantom_s : std:: marker:: PhantomData < S > ,
183
184
}
184
185
185
186
/// Represents a syntactically and semantically correct lightning BOLT11 invoice.
@@ -432,7 +433,7 @@ pub mod constants {
432
433
pub const TAG_FEATURES : u8 = 5 ;
433
434
}
434
435
435
- impl InvoiceBuilder < tb:: False , tb:: False , tb:: False , tb:: False > {
436
+ impl InvoiceBuilder < tb:: False , tb:: False , tb:: False , tb:: False , tb :: False > {
436
437
/// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before
437
438
/// `InvoiceBuilder::build(self)` becomes available.
438
439
pub fn new ( currrency : Currency ) -> Self {
@@ -448,14 +449,15 @@ impl InvoiceBuilder<tb::False, tb::False, tb::False, tb::False> {
448
449
phantom_h : std:: marker:: PhantomData ,
449
450
phantom_t : std:: marker:: PhantomData ,
450
451
phantom_c : std:: marker:: PhantomData ,
452
+ phantom_s : std:: marker:: PhantomData ,
451
453
}
452
454
}
453
455
}
454
456
455
- impl < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool , C : tb:: Bool > InvoiceBuilder < D , H , T , C > {
457
+ impl < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool , C : tb:: Bool , S : tb :: Bool > InvoiceBuilder < D , H , T , C , S > {
456
458
/// Helper function to set the completeness flags.
457
- fn set_flags < DN : tb:: Bool , HN : tb:: Bool , TN : tb:: Bool , CN : tb:: Bool > ( self ) -> InvoiceBuilder < DN , HN , TN , CN > {
458
- InvoiceBuilder :: < DN , HN , TN , CN > {
459
+ fn set_flags < DN : tb:: Bool , HN : tb:: Bool , TN : tb:: Bool , CN : tb:: Bool , SN : tb :: Bool > ( self ) -> InvoiceBuilder < DN , HN , TN , CN , SN > {
460
+ InvoiceBuilder :: < DN , HN , TN , CN , SN > {
459
461
currency : self . currency ,
460
462
amount : self . amount ,
461
463
si_prefix : self . si_prefix ,
@@ -467,6 +469,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T,
467
469
phantom_h : std:: marker:: PhantomData ,
468
470
phantom_t : std:: marker:: PhantomData ,
469
471
phantom_c : std:: marker:: PhantomData ,
472
+ phantom_s : std:: marker:: PhantomData ,
470
473
}
471
474
}
472
475
@@ -487,12 +490,6 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T,
487
490
self
488
491
}
489
492
490
- /// Sets the payment secret
491
- pub fn payment_secret ( mut self , payment_secret : PaymentSecret ) -> Self {
492
- self . tagged_fields . push ( TaggedField :: PaymentSecret ( payment_secret) ) ;
493
- self
494
- }
495
-
496
493
/// Sets the expiry time
497
494
pub fn expiry_time ( mut self , expiry_time : Duration ) -> Self {
498
495
match ExpiryTime :: from_duration ( expiry_time) {
@@ -516,16 +513,9 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T,
516
513
}
517
514
self
518
515
}
519
-
520
- /// Adds a features field which indicates the set of supported protocol extensions which the
521
- /// origin node supports.
522
- pub fn features ( mut self , features : InvoiceFeatures ) -> Self {
523
- self . tagged_fields . push ( TaggedField :: Features ( features) ) ;
524
- self
525
- }
526
516
}
527
517
528
- impl < D : tb:: Bool , H : tb:: Bool , C : tb:: Bool > InvoiceBuilder < D , H , tb:: True , C > {
518
+ impl < D : tb:: Bool , H : tb:: Bool , C : tb:: Bool , S : tb :: Bool > InvoiceBuilder < D , H , tb:: True , C , S > {
529
519
/// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields.
530
520
pub fn build_raw ( self ) -> Result < RawInvoice , CreationError > {
531
521
@@ -558,9 +548,9 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, tb::True, C> {
558
548
}
559
549
}
560
550
561
- impl < H : tb:: Bool , T : tb:: Bool , C : tb:: Bool > InvoiceBuilder < tb:: False , H , T , C > {
551
+ impl < H : tb:: Bool , T : tb:: Bool , C : tb:: Bool , S : tb :: Bool > InvoiceBuilder < tb:: False , H , T , C , S > {
562
552
/// Set the description. This function is only available if no description (hash) was set.
563
- pub fn description ( mut self , description : String ) -> InvoiceBuilder < tb:: True , H , T , C > {
553
+ pub fn description ( mut self , description : String ) -> InvoiceBuilder < tb:: True , H , T , C , S > {
564
554
match Description :: new ( description) {
565
555
Ok ( d) => self . tagged_fields . push ( TaggedField :: Description ( d) ) ,
566
556
Err ( e) => self . error = Some ( e) ,
@@ -569,23 +559,23 @@ impl<H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<tb::False, H, T, C> {
569
559
}
570
560
571
561
/// Set the description hash. This function is only available if no description (hash) was set.
572
- pub fn description_hash ( mut self , description_hash : sha256:: Hash ) -> InvoiceBuilder < tb:: True , H , T , C > {
562
+ pub fn description_hash ( mut self , description_hash : sha256:: Hash ) -> InvoiceBuilder < tb:: True , H , T , C , S > {
573
563
self . tagged_fields . push ( TaggedField :: DescriptionHash ( Sha256 ( description_hash) ) ) ;
574
564
self . set_flags ( )
575
565
}
576
566
}
577
567
578
- impl < D : tb:: Bool , T : tb:: Bool , C : tb:: Bool > InvoiceBuilder < D , tb:: False , T , C > {
568
+ impl < D : tb:: Bool , T : tb:: Bool , C : tb:: Bool , S : tb :: Bool > InvoiceBuilder < D , tb:: False , T , C , S > {
579
569
/// Set the payment hash. This function is only available if no payment hash was set.
580
- pub fn payment_hash ( mut self , hash : sha256:: Hash ) -> InvoiceBuilder < D , tb:: True , T , C > {
570
+ pub fn payment_hash ( mut self , hash : sha256:: Hash ) -> InvoiceBuilder < D , tb:: True , T , C , S > {
581
571
self . tagged_fields . push ( TaggedField :: PaymentHash ( Sha256 ( hash) ) ) ;
582
572
self . set_flags ( )
583
573
}
584
574
}
585
575
586
- impl < D : tb:: Bool , H : tb:: Bool , C : tb:: Bool > InvoiceBuilder < D , H , tb:: False , C > {
576
+ impl < D : tb:: Bool , H : tb:: Bool , C : tb:: Bool , S : tb :: Bool > InvoiceBuilder < D , H , tb:: False , C , S > {
587
577
/// Sets the timestamp.
588
- pub fn timestamp ( mut self , time : SystemTime ) -> InvoiceBuilder < D , H , tb:: True , C > {
578
+ pub fn timestamp ( mut self , time : SystemTime ) -> InvoiceBuilder < D , H , tb:: True , C , S > {
589
579
match PositiveTimestamp :: from_system_time ( time) {
590
580
Ok ( t) => self . timestamp = Some ( t) ,
591
581
Err ( e) => self . error = Some ( e) ,
@@ -595,22 +585,48 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, tb::False, C> {
595
585
}
596
586
597
587
/// Sets the timestamp to the current UNIX timestamp.
598
- pub fn current_timestamp ( mut self ) -> InvoiceBuilder < D , H , tb:: True , C > {
588
+ pub fn current_timestamp ( mut self ) -> InvoiceBuilder < D , H , tb:: True , C , S > {
599
589
let now = PositiveTimestamp :: from_system_time ( SystemTime :: now ( ) ) ;
600
590
self . timestamp = Some ( now. expect ( "for the foreseeable future this shouldn't happen" ) ) ;
601
591
self . set_flags ( )
602
592
}
603
593
}
604
594
605
- impl < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool > InvoiceBuilder < D , H , T , tb:: False > {
595
+ impl < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool , S : tb :: Bool > InvoiceBuilder < D , H , T , tb:: False , S > {
606
596
/// Sets `min_final_cltv_expiry`.
607
- pub fn min_final_cltv_expiry ( mut self , min_final_cltv_expiry : u64 ) -> InvoiceBuilder < D , H , T , tb:: True > {
597
+ pub fn min_final_cltv_expiry ( mut self , min_final_cltv_expiry : u64 ) -> InvoiceBuilder < D , H , T , tb:: True , S > {
608
598
self . tagged_fields . push ( TaggedField :: MinFinalCltvExpiry ( MinFinalCltvExpiry ( min_final_cltv_expiry) ) ) ;
609
599
self . set_flags ( )
610
600
}
611
601
}
612
602
613
- impl InvoiceBuilder < tb:: True , tb:: True , tb:: True , tb:: True > {
603
+ impl < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool , C : tb:: Bool > InvoiceBuilder < D , H , T , C , tb:: False > {
604
+ /// Sets the payment secret and relevant features.
605
+ pub fn payment_secret ( mut self , payment_secret : PaymentSecret ) -> InvoiceBuilder < D , H , T , C , tb:: True > {
606
+ let features = InvoiceFeatures :: empty ( )
607
+ . set_variable_length_onion_required ( )
608
+ . set_payment_secret_required ( ) ;
609
+ self . tagged_fields . push ( TaggedField :: PaymentSecret ( payment_secret) ) ;
610
+ self . tagged_fields . push ( TaggedField :: Features ( features) ) ;
611
+ self . set_flags ( )
612
+ }
613
+ }
614
+
615
+ impl < D : tb:: Bool , H : tb:: Bool , T : tb:: Bool , C : tb:: Bool > InvoiceBuilder < D , H , T , C , tb:: True > {
616
+ /// Sets the `basic_mpp` feature as optional.
617
+ pub fn basic_mpp ( mut self ) -> Self {
618
+ self . tagged_fields = self . tagged_fields
619
+ . drain ( ..)
620
+ . map ( |field| match field {
621
+ TaggedField :: Features ( f) => TaggedField :: Features ( f. set_basic_mpp_optional ( ) ) ,
622
+ _ => field,
623
+ } )
624
+ . collect ( ) ;
625
+ self
626
+ }
627
+ }
628
+
629
+ impl < S : tb:: Bool > InvoiceBuilder < tb:: True , tb:: True , tb:: True , tb:: True , S > {
614
630
/// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
615
631
/// and MUST produce a recoverable signature valid for the given hash and if applicable also for
616
632
/// the included payee public key.
@@ -649,6 +665,7 @@ impl InvoiceBuilder<tb::True, tb::True, tb::True, tb::True> {
649
665
} ;
650
666
651
667
invoice. check_field_counts ( ) . expect ( "should be ensured by type signature of builder" ) ;
668
+ invoice. check_feature_bits ( ) . expect ( "should be ensured by type signature of builder" ) ;
652
669
653
670
Ok ( invoice)
654
671
}
@@ -977,6 +994,41 @@ impl Invoice {
977
994
Ok ( ( ) )
978
995
}
979
996
997
+ /// Check that feature bits are set as required
998
+ fn check_feature_bits ( & self ) -> Result < ( ) , SemanticError > {
999
+ // "If the payment_secret feature is set, MUST include exactly one s field."
1000
+ let payment_secret_count = self . tagged_fields ( ) . filter ( |& tf| match * tf {
1001
+ TaggedField :: PaymentSecret ( _) => true ,
1002
+ _ => false ,
1003
+ } ) . count ( ) ;
1004
+ if payment_secret_count > 1 {
1005
+ return Err ( SemanticError :: MultiplePaymentSecrets ) ;
1006
+ }
1007
+
1008
+ // "A writer MUST set an s field if and only if the payment_secret feature is set."
1009
+ let has_payment_secret = payment_secret_count == 1 ;
1010
+ let features = self . tagged_fields ( ) . find ( |& tf| match * tf {
1011
+ TaggedField :: Features ( _) => true ,
1012
+ _ => false ,
1013
+ } ) ;
1014
+ match features {
1015
+ None if has_payment_secret => Err ( SemanticError :: InvalidFeatures ) ,
1016
+ None => Ok ( ( ) ) ,
1017
+ Some ( TaggedField :: Features ( features) ) => {
1018
+ if features. supports_payment_secret ( ) && has_payment_secret {
1019
+ Ok ( ( ) )
1020
+ } else if has_payment_secret {
1021
+ Err ( SemanticError :: InvalidFeatures )
1022
+ } else if features. supports_payment_secret ( ) {
1023
+ Err ( SemanticError :: InvalidFeatures )
1024
+ } else {
1025
+ Ok ( ( ) )
1026
+ }
1027
+ } ,
1028
+ Some ( _) => unreachable ! ( ) ,
1029
+ }
1030
+ }
1031
+
980
1032
/// Check that the invoice is signed correctly and that key recovery works
981
1033
pub fn check_signature ( & self ) -> Result < ( ) , SemanticError > {
982
1034
match self . signed_invoice . recover_payee_pub_key ( ) {
@@ -1011,6 +1063,7 @@ impl Invoice {
1011
1063
signed_invoice : signed_invoice,
1012
1064
} ;
1013
1065
invoice. check_field_counts ( ) ?;
1066
+ invoice. check_feature_bits ( ) ?;
1014
1067
invoice. check_signature ( ) ?;
1015
1068
1016
1069
Ok ( invoice)
@@ -1303,6 +1356,12 @@ pub enum SemanticError {
1303
1356
/// The invoice contains multiple descriptions and/or description hashes which isn't allowed
1304
1357
MultipleDescriptions ,
1305
1358
1359
+ /// The invoice contains multiple payment secrets
1360
+ MultiplePaymentSecrets ,
1361
+
1362
+ /// The invoice's features are invalid
1363
+ InvalidFeatures ,
1364
+
1306
1365
/// The recovery id doesn't fit the signature/pub key
1307
1366
InvalidRecoveryId ,
1308
1367
@@ -1317,6 +1376,8 @@ impl Display for SemanticError {
1317
1376
SemanticError :: MultiplePaymentHashes => f. write_str ( "The invoice has multiple payment hashes which isn't allowed" ) ,
1318
1377
SemanticError :: NoDescription => f. write_str ( "No description or description hash are part of the invoice" ) ,
1319
1378
SemanticError :: MultipleDescriptions => f. write_str ( "The invoice contains multiple descriptions and/or description hashes which isn't allowed" ) ,
1379
+ SemanticError :: MultiplePaymentSecrets => f. write_str ( "The invoice contains multiple payment secrets" ) ,
1380
+ SemanticError :: InvalidFeatures => f. write_str ( "The invoice's features are invalid" ) ,
1320
1381
SemanticError :: InvalidRecoveryId => f. write_str ( "The recovery id doesn't fit the signature/pub key" ) ,
1321
1382
SemanticError :: InvalidSignature => f. write_str ( "The invoice's signature is invalid" ) ,
1322
1383
}
@@ -1467,6 +1528,97 @@ mod test {
1467
1528
assert ! ( new_signed. check_signature( ) ) ;
1468
1529
}
1469
1530
1531
+ #[ test]
1532
+ fn test_check_feature_bits ( ) {
1533
+ use TaggedField :: * ;
1534
+ use lightning:: ln:: features:: InvoiceFeatures ;
1535
+ use secp256k1:: Secp256k1 ;
1536
+ use secp256k1:: key:: SecretKey ;
1537
+ use { RawInvoice , RawHrp , RawDataPart , Currency , Sha256 , PositiveTimestamp , Invoice ,
1538
+ SemanticError } ;
1539
+
1540
+ let private_key = SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ;
1541
+ let payment_secret = lightning:: ln:: PaymentSecret ( [ 21 ; 32 ] ) ;
1542
+ let invoice_template = RawInvoice {
1543
+ hrp : RawHrp {
1544
+ currency : Currency :: Bitcoin ,
1545
+ raw_amount : None ,
1546
+ si_prefix : None ,
1547
+ } ,
1548
+ data : RawDataPart {
1549
+ timestamp : PositiveTimestamp :: from_unix_timestamp ( 1496314658 ) . unwrap ( ) ,
1550
+ tagged_fields : vec ! [
1551
+ PaymentHash ( Sha256 ( sha256:: Hash :: from_hex(
1552
+ "0001020304050607080900010203040506070809000102030405060708090102"
1553
+ ) . unwrap( ) ) ) . into( ) ,
1554
+ Description (
1555
+ :: Description :: new(
1556
+ "Please consider supporting this project" . to_owned( )
1557
+ ) . unwrap( )
1558
+ ) . into( ) ,
1559
+ ] ,
1560
+ } ,
1561
+ } ;
1562
+
1563
+ // Missing features
1564
+ let invoice = {
1565
+ let mut invoice = invoice_template. clone ( ) ;
1566
+ invoice. data . tagged_fields . push ( PaymentSecret ( payment_secret) . into ( ) ) ;
1567
+ invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1568
+ } . unwrap ( ) ;
1569
+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: InvalidFeatures ) ) ;
1570
+
1571
+ // Missing feature bits
1572
+ let invoice = {
1573
+ let mut invoice = invoice_template. clone ( ) ;
1574
+ invoice. data . tagged_fields . push ( PaymentSecret ( payment_secret) . into ( ) ) ;
1575
+ invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: empty ( ) ) . into ( ) ) ;
1576
+ invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1577
+ } . unwrap ( ) ;
1578
+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: InvalidFeatures ) ) ;
1579
+
1580
+ // Including payment secret and feature bits
1581
+ let invoice = {
1582
+ let mut invoice = invoice_template. clone ( ) ;
1583
+ invoice. data . tagged_fields . push ( PaymentSecret ( payment_secret) . into ( ) ) ;
1584
+ invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: known ( ) ) . into ( ) ) ;
1585
+ invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1586
+ } . unwrap ( ) ;
1587
+ assert ! ( Invoice :: from_signed( invoice) . is_ok( ) ) ;
1588
+
1589
+ // No payment secret or features
1590
+ let invoice = {
1591
+ let invoice = invoice_template. clone ( ) ;
1592
+ invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1593
+ } . unwrap ( ) ;
1594
+ assert ! ( Invoice :: from_signed( invoice) . is_ok( ) ) ;
1595
+
1596
+ // No payment secret or feature bits
1597
+ let invoice = {
1598
+ let mut invoice = invoice_template. clone ( ) ;
1599
+ invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: empty ( ) ) . into ( ) ) ;
1600
+ invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1601
+ } . unwrap ( ) ;
1602
+ assert ! ( Invoice :: from_signed( invoice) . is_ok( ) ) ;
1603
+
1604
+ // Missing payment secret
1605
+ let invoice = {
1606
+ let mut invoice = invoice_template. clone ( ) ;
1607
+ invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: known ( ) ) . into ( ) ) ;
1608
+ invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1609
+ } . unwrap ( ) ;
1610
+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: InvalidFeatures ) ) ;
1611
+
1612
+ // Multiple payment secrets
1613
+ let invoice = {
1614
+ let mut invoice = invoice_template. clone ( ) ;
1615
+ invoice. data . tagged_fields . push ( PaymentSecret ( payment_secret) . into ( ) ) ;
1616
+ invoice. data . tagged_fields . push ( PaymentSecret ( payment_secret) . into ( ) ) ;
1617
+ invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1618
+ } . unwrap ( ) ;
1619
+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: MultiplePaymentSecrets ) ) ;
1620
+ }
1621
+
1470
1622
#[ test]
1471
1623
fn test_builder_amount ( ) {
1472
1624
use :: * ;
@@ -1624,14 +1776,16 @@ mod test {
1624
1776
. route ( route_1. clone ( ) )
1625
1777
. route ( route_2. clone ( ) )
1626
1778
. description_hash ( sha256:: Hash :: from_slice ( & [ 3 ; 32 ] [ ..] ) . unwrap ( ) )
1627
- . payment_hash ( sha256:: Hash :: from_slice ( & [ 21 ; 32 ] [ ..] ) . unwrap ( ) ) ;
1779
+ . payment_hash ( sha256:: Hash :: from_slice ( & [ 21 ; 32 ] [ ..] ) . unwrap ( ) )
1780
+ . payment_secret ( PaymentSecret ( [ 42 ; 32 ] ) )
1781
+ . basic_mpp ( ) ;
1628
1782
1629
1783
let invoice = builder. clone ( ) . build_signed ( |hash| {
1630
1784
secp_ctx. sign_recoverable ( hash, & private_key)
1631
1785
} ) . unwrap ( ) ;
1632
1786
1633
1787
assert ! ( invoice. check_signature( ) . is_ok( ) ) ;
1634
- assert_eq ! ( invoice. tagged_fields( ) . count( ) , 8 ) ;
1788
+ assert_eq ! ( invoice. tagged_fields( ) . count( ) , 10 ) ;
1635
1789
1636
1790
assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 123 ) ) ;
1637
1791
assert_eq ! ( invoice. currency( ) , Currency :: BitcoinTestnet ) ;
@@ -1649,6 +1803,8 @@ mod test {
1649
1803
InvoiceDescription :: Hash ( & Sha256 ( sha256:: Hash :: from_slice( & [ 3 ; 32 ] [ ..] ) . unwrap( ) ) )
1650
1804
) ;
1651
1805
assert_eq ! ( invoice. payment_hash( ) , & sha256:: Hash :: from_slice( & [ 21 ; 32 ] [ ..] ) . unwrap( ) ) ;
1806
+ assert_eq ! ( invoice. payment_secret( ) , Some ( & PaymentSecret ( [ 42 ; 32 ] ) ) ) ;
1807
+ assert_eq ! ( invoice. features( ) , Some ( & InvoiceFeatures :: known( ) ) ) ;
1652
1808
1653
1809
let raw_invoice = builder. build_raw ( ) . unwrap ( ) ;
1654
1810
assert_eq ! ( raw_invoice, * invoice. into_signed_raw( ) . raw_invoice( ) )
0 commit comments