@@ -127,6 +127,7 @@ pub fn check_platform() {
127127///
128128/// ```
129129/// extern crate secp256k1;
130+ /// extern crate lightning;
130131/// extern crate lightning_invoice;
131132/// extern crate bitcoin_hashes;
132133///
@@ -136,6 +137,8 @@ pub fn check_platform() {
136137/// use secp256k1::Secp256k1;
137138/// use secp256k1::key::SecretKey;
138139///
140+ /// use lightning::ln::PaymentSecret;
141+ ///
139142/// use lightning_invoice::{Currency, InvoiceBuilder};
140143///
141144/// # fn main() {
@@ -148,10 +151,12 @@ pub fn check_platform() {
148151/// ).unwrap();
149152///
150153/// let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
154+ /// let payment_secret = PaymentSecret([42u8; 32]);
151155///
152156/// let invoice = InvoiceBuilder::new(Currency::Bitcoin)
153157/// .description("Coins pls!".into())
154158/// .payment_hash(payment_hash)
159+ /// .payment_secret(payment_secret)
155160/// .current_timestamp()
156161/// .min_final_cltv_expiry(144)
157162/// .build_signed(|hash| {
@@ -634,7 +639,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T,
634639 }
635640}
636641
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 > {
638643 /// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
639644 /// and MUST produce a recoverable signature valid for the given hash and if applicable also for
640645 /// the included payee public key.
@@ -1018,6 +1023,24 @@ impl Invoice {
10181023 return Err ( SemanticError :: MultipleDescriptions ) ;
10191024 }
10201025
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+
10211044 Ok ( ( ) )
10221045 }
10231046
@@ -1033,32 +1056,21 @@ impl Invoice {
10331056
10341057 /// Check that feature bits are set as required
10351058 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 ( ) ?;
10441060
10451061 // "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).
10471064 let features = self . tagged_fields ( ) . find ( |& tf| match * tf {
10481065 TaggedField :: Features ( _) => true ,
10491066 _ => false ,
10501067 } ) ;
10511068 match features {
1052- None if has_payment_secret => Err ( SemanticError :: InvalidFeatures ) ,
1053- None => Ok ( ( ) ) ,
1069+ None => Err ( SemanticError :: InvalidFeatures ) ,
10541070 Some ( TaggedField :: Features ( features) ) => {
10551071 if features. requires_unknown_bits ( ) {
10561072 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 ( ) {
10621074 Err ( SemanticError :: InvalidFeatures )
10631075 } else {
10641076 Ok ( ( ) )
@@ -1154,8 +1166,8 @@ impl Invoice {
11541166 }
11551167
11561168 /// 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" )
11591171 }
11601172
11611173 /// Get the invoice features if they were included in the invoice
@@ -1412,6 +1424,10 @@ pub enum SemanticError {
14121424 /// The invoice contains multiple descriptions and/or description hashes which isn't allowed
14131425 MultipleDescriptions ,
14141426
1427+ /// The invoice is missing the mandatory payment secret, which all modern lightning nodes
1428+ /// should provide.
1429+ NoPaymentSecret ,
1430+
14151431 /// The invoice contains multiple payment secrets
14161432 MultiplePaymentSecrets ,
14171433
@@ -1435,6 +1451,7 @@ impl Display for SemanticError {
14351451 SemanticError :: MultiplePaymentHashes => f. write_str ( "The invoice has multiple payment hashes which isn't allowed" ) ,
14361452 SemanticError :: NoDescription => f. write_str ( "No description or description hash are part of the invoice" ) ,
14371453 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" ) ,
14381455 SemanticError :: MultiplePaymentSecrets => f. write_str ( "The invoice contains multiple payment secrets" ) ,
14391456 SemanticError :: InvalidFeatures => f. write_str ( "The invoice's features are invalid" ) ,
14401457 SemanticError :: InvalidRecoveryId => f. write_str ( "The recovery id doesn't fit the signature/pub key" ) ,
@@ -1651,23 +1668,23 @@ mod test {
16511668 let invoice = invoice_template. clone ( ) ;
16521669 invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
16531670 } . unwrap ( ) ;
1654- assert ! ( Invoice :: from_signed( invoice) . is_ok ( ) ) ;
1671+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
16551672
16561673 // No payment secret or feature bits
16571674 let invoice = {
16581675 let mut invoice = invoice_template. clone ( ) ;
16591676 invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: empty ( ) ) . into ( ) ) ;
16601677 invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
16611678 } . unwrap ( ) ;
1662- assert ! ( Invoice :: from_signed( invoice) . is_ok ( ) ) ;
1679+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
16631680
16641681 // Missing payment secret
16651682 let invoice = {
16661683 let mut invoice = invoice_template. clone ( ) ;
16671684 invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: known ( ) ) . into ( ) ) ;
16681685 invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
16691686 } . unwrap ( ) ;
1670- assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: InvalidFeatures ) ) ;
1687+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
16711688
16721689 // Multiple payment secrets
16731690 let invoice = {
@@ -1753,6 +1770,7 @@ mod test {
17531770
17541771 let sign_error_res = builder. clone ( )
17551772 . description ( "Test" . into ( ) )
1773+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
17561774 . try_build_signed ( |_| {
17571775 Err ( "ImaginaryError" )
17581776 } ) ;
@@ -1865,7 +1883,7 @@ mod test {
18651883 InvoiceDescription :: Hash ( & Sha256 ( sha256:: Hash :: from_slice( & [ 3 ; 32 ] [ ..] ) . unwrap( ) ) )
18661884 ) ;
18671885 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 ] ) ) ;
18691887 assert_eq ! ( invoice. features( ) , Some ( & InvoiceFeatures :: known( ) ) ) ;
18701888
18711889 let raw_invoice = builder. build_raw ( ) . unwrap ( ) ;
@@ -1881,6 +1899,7 @@ mod test {
18811899 let signed_invoice = InvoiceBuilder :: new ( Currency :: Bitcoin )
18821900 . description ( "Test" . into ( ) )
18831901 . payment_hash ( sha256:: Hash :: from_slice ( & [ 0 ; 32 ] [ ..] ) . unwrap ( ) )
1902+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
18841903 . current_timestamp ( )
18851904 . build_raw ( )
18861905 . unwrap ( )
0 commit comments