@@ -127,6 +127,7 @@ pub fn check_platform() {
127
127
///
128
128
/// ```
129
129
/// extern crate secp256k1;
130
+ /// extern crate lightning;
130
131
/// extern crate lightning_invoice;
131
132
/// extern crate bitcoin_hashes;
132
133
///
@@ -136,6 +137,8 @@ pub fn check_platform() {
136
137
/// use secp256k1::Secp256k1;
137
138
/// use secp256k1::key::SecretKey;
138
139
///
140
+ /// use lightning::ln::PaymentSecret;
141
+ ///
139
142
/// use lightning_invoice::{Currency, InvoiceBuilder};
140
143
///
141
144
/// # fn main() {
@@ -148,10 +151,12 @@ pub fn check_platform() {
148
151
/// ).unwrap();
149
152
///
150
153
/// let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
154
+ /// let payment_secret = PaymentSecret([42u8; 32]);
151
155
///
152
156
/// let invoice = InvoiceBuilder::new(Currency::Bitcoin)
153
157
/// .description("Coins pls!".into())
154
158
/// .payment_hash(payment_hash)
159
+ /// .payment_secret(payment_secret)
155
160
/// .current_timestamp()
156
161
/// .min_final_cltv_expiry(144)
157
162
/// .build_signed(|hash| {
@@ -634,7 +639,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T,
634
639
}
635
640
}
636
641
637
- impl < S : tb :: Bool > InvoiceBuilder < tb:: True , tb:: True , tb:: True , tb:: True , S > {
642
+ impl InvoiceBuilder < tb:: True , tb:: True , tb:: True , tb:: True , tb :: True > {
638
643
/// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
639
644
/// and MUST produce a recoverable signature valid for the given hash and if applicable also for
640
645
/// the included payee public key.
@@ -1018,6 +1023,24 @@ impl Invoice {
1018
1023
return Err ( SemanticError :: MultipleDescriptions ) ;
1019
1024
}
1020
1025
1026
+ self . check_payment_secret ( ) ?;
1027
+
1028
+ Ok ( ( ) )
1029
+ }
1030
+
1031
+ /// Checks that there is exactly one payment secret field
1032
+ fn check_payment_secret ( & self ) -> Result < ( ) , SemanticError > {
1033
+ // "A writer MUST include exactly one `s` field."
1034
+ let payment_secret_count = self . tagged_fields ( ) . filter ( |& tf| match * tf {
1035
+ TaggedField :: PaymentSecret ( _) => true ,
1036
+ _ => false ,
1037
+ } ) . count ( ) ;
1038
+ if payment_secret_count < 1 {
1039
+ return Err ( SemanticError :: NoPaymentSecret ) ;
1040
+ } else if payment_secret_count > 1 {
1041
+ return Err ( SemanticError :: MultiplePaymentSecrets ) ;
1042
+ }
1043
+
1021
1044
Ok ( ( ) )
1022
1045
}
1023
1046
@@ -1033,32 +1056,21 @@ impl Invoice {
1033
1056
1034
1057
/// Check that feature bits are set as required
1035
1058
fn check_feature_bits ( & self ) -> Result < ( ) , SemanticError > {
1036
- // "If the payment_secret feature is set, MUST include exactly one s field."
1037
- let payment_secret_count = self . tagged_fields ( ) . filter ( |& tf| match * tf {
1038
- TaggedField :: PaymentSecret ( _) => true ,
1039
- _ => false ,
1040
- } ) . count ( ) ;
1041
- if payment_secret_count > 1 {
1042
- return Err ( SemanticError :: MultiplePaymentSecrets ) ;
1043
- }
1059
+ self . check_payment_secret ( ) ?;
1044
1060
1045
1061
// "A writer MUST set an s field if and only if the payment_secret feature is set."
1046
- let has_payment_secret = payment_secret_count == 1 ;
1062
+ // (this requirement has been since removed, and we now require the payment secret
1063
+ // feature bit always).
1047
1064
let features = self . tagged_fields ( ) . find ( |& tf| match * tf {
1048
1065
TaggedField :: Features ( _) => true ,
1049
1066
_ => false ,
1050
1067
} ) ;
1051
1068
match features {
1052
- None if has_payment_secret => Err ( SemanticError :: InvalidFeatures ) ,
1053
- None => Ok ( ( ) ) ,
1069
+ None => Err ( SemanticError :: InvalidFeatures ) ,
1054
1070
Some ( TaggedField :: Features ( features) ) => {
1055
1071
if features. requires_unknown_bits ( ) {
1056
1072
Err ( SemanticError :: InvalidFeatures )
1057
- } else if features. supports_payment_secret ( ) && has_payment_secret {
1058
- Ok ( ( ) )
1059
- } else if has_payment_secret {
1060
- Err ( SemanticError :: InvalidFeatures )
1061
- } else if features. supports_payment_secret ( ) {
1073
+ } else if !features. supports_payment_secret ( ) {
1062
1074
Err ( SemanticError :: InvalidFeatures )
1063
1075
} else {
1064
1076
Ok ( ( ) )
@@ -1154,8 +1166,8 @@ impl Invoice {
1154
1166
}
1155
1167
1156
1168
/// Get the payment secret if one was included in the invoice
1157
- pub fn payment_secret ( & self ) -> Option < & PaymentSecret > {
1158
- self . signed_invoice . payment_secret ( )
1169
+ pub fn payment_secret ( & self ) -> & PaymentSecret {
1170
+ self . signed_invoice . payment_secret ( ) . expect ( "was checked by constructor" )
1159
1171
}
1160
1172
1161
1173
/// Get the invoice features if they were included in the invoice
@@ -1412,6 +1424,10 @@ pub enum SemanticError {
1412
1424
/// The invoice contains multiple descriptions and/or description hashes which isn't allowed
1413
1425
MultipleDescriptions ,
1414
1426
1427
+ /// The invoice is missing the mandatory payment secret, which all modern lightning nodes
1428
+ /// should provide.
1429
+ NoPaymentSecret ,
1430
+
1415
1431
/// The invoice contains multiple payment secrets
1416
1432
MultiplePaymentSecrets ,
1417
1433
@@ -1435,6 +1451,7 @@ impl Display for SemanticError {
1435
1451
SemanticError :: MultiplePaymentHashes => f. write_str ( "The invoice has multiple payment hashes which isn't allowed" ) ,
1436
1452
SemanticError :: NoDescription => f. write_str ( "No description or description hash are part of the invoice" ) ,
1437
1453
SemanticError :: MultipleDescriptions => f. write_str ( "The invoice contains multiple descriptions and/or description hashes which isn't allowed" ) ,
1454
+ SemanticError :: NoPaymentSecret => f. write_str ( "The invoice is missing the mandatory payment secret" ) ,
1438
1455
SemanticError :: MultiplePaymentSecrets => f. write_str ( "The invoice contains multiple payment secrets" ) ,
1439
1456
SemanticError :: InvalidFeatures => f. write_str ( "The invoice's features are invalid" ) ,
1440
1457
SemanticError :: InvalidRecoveryId => f. write_str ( "The recovery id doesn't fit the signature/pub key" ) ,
@@ -1651,23 +1668,23 @@ mod test {
1651
1668
let invoice = invoice_template. clone ( ) ;
1652
1669
invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1653
1670
} . unwrap ( ) ;
1654
- assert ! ( Invoice :: from_signed( invoice) . is_ok ( ) ) ;
1671
+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
1655
1672
1656
1673
// No payment secret or feature bits
1657
1674
let invoice = {
1658
1675
let mut invoice = invoice_template. clone ( ) ;
1659
1676
invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: empty ( ) ) . into ( ) ) ;
1660
1677
invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1661
1678
} . unwrap ( ) ;
1662
- assert ! ( Invoice :: from_signed( invoice) . is_ok ( ) ) ;
1679
+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
1663
1680
1664
1681
// Missing payment secret
1665
1682
let invoice = {
1666
1683
let mut invoice = invoice_template. clone ( ) ;
1667
1684
invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: known ( ) ) . into ( ) ) ;
1668
1685
invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
1669
1686
} . unwrap ( ) ;
1670
- assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: InvalidFeatures ) ) ;
1687
+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
1671
1688
1672
1689
// Multiple payment secrets
1673
1690
let invoice = {
@@ -1753,6 +1770,7 @@ mod test {
1753
1770
1754
1771
let sign_error_res = builder. clone ( )
1755
1772
. description ( "Test" . into ( ) )
1773
+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
1756
1774
. try_build_signed ( |_| {
1757
1775
Err ( "ImaginaryError" )
1758
1776
} ) ;
@@ -1865,7 +1883,7 @@ mod test {
1865
1883
InvoiceDescription :: Hash ( & Sha256 ( sha256:: Hash :: from_slice( & [ 3 ; 32 ] [ ..] ) . unwrap( ) ) )
1866
1884
) ;
1867
1885
assert_eq ! ( invoice. payment_hash( ) , & sha256:: Hash :: from_slice( & [ 21 ; 32 ] [ ..] ) . unwrap( ) ) ;
1868
- assert_eq ! ( invoice. payment_secret( ) , Some ( & PaymentSecret ( [ 42 ; 32 ] ) ) ) ;
1886
+ assert_eq ! ( invoice. payment_secret( ) , & PaymentSecret ( [ 42 ; 32 ] ) ) ;
1869
1887
assert_eq ! ( invoice. features( ) , Some ( & InvoiceFeatures :: known( ) ) ) ;
1870
1888
1871
1889
let raw_invoice = builder. build_raw ( ) . unwrap ( ) ;
@@ -1881,6 +1899,7 @@ mod test {
1881
1899
let signed_invoice = InvoiceBuilder :: new ( Currency :: Bitcoin )
1882
1900
. description ( "Test" . into ( ) )
1883
1901
. payment_hash ( sha256:: Hash :: from_slice ( & [ 0 ; 32 ] [ ..] ) . unwrap ( ) )
1902
+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
1884
1903
. current_timestamp ( )
1885
1904
. build_raw ( )
1886
1905
. unwrap ( )
0 commit comments