@@ -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,17 @@ impl Invoice {
10181023 return Err ( SemanticError :: MultipleDescriptions ) ;
10191024 }
10201025
1026+ // "A writer MUST include exactly one `s` field."
1027+ let payment_secret_count = self . tagged_fields ( ) . filter ( |& tf| match * tf {
1028+ TaggedField :: PaymentSecret ( _) => true ,
1029+ _ => false ,
1030+ } ) . count ( ) ;
1031+ if payment_secret_count < 1 {
1032+ return Err ( SemanticError :: NoPaymentSecret ) ;
1033+ } else if payment_secret_count > 1 {
1034+ return Err ( SemanticError :: MultiplePaymentSecrets ) ;
1035+ }
1036+
10211037 Ok ( ( ) )
10221038 }
10231039
@@ -1033,32 +1049,30 @@ impl Invoice {
10331049
10341050 /// Check that feature bits are set as required
10351051 fn check_feature_bits ( & self ) -> Result < ( ) , SemanticError > {
1036- // "If the payment_secret feature is set, MUST include exactly one s field."
1052+ // "A writer MUST include exactly one `s` field."
10371053 let payment_secret_count = self . tagged_fields ( ) . filter ( |& tf| match * tf {
10381054 TaggedField :: PaymentSecret ( _) => true ,
10391055 _ => false ,
10401056 } ) . count ( ) ;
1041- if payment_secret_count > 1 {
1057+ if payment_secret_count < 1 {
1058+ return Err ( SemanticError :: NoPaymentSecret ) ;
1059+ } else if payment_secret_count > 1 {
10421060 return Err ( SemanticError :: MultiplePaymentSecrets ) ;
10431061 }
10441062
10451063 // "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 ;
1064+ // (this requirement has been since removed, and we now require the payment secret
1065+ // feature bit always).
10471066 let features = self . tagged_fields ( ) . find ( |& tf| match * tf {
10481067 TaggedField :: Features ( _) => true ,
10491068 _ => false ,
10501069 } ) ;
10511070 match features {
1052- None if has_payment_secret => Err ( SemanticError :: InvalidFeatures ) ,
1053- None => Ok ( ( ) ) ,
1071+ None => Err ( SemanticError :: InvalidFeatures ) ,
10541072 Some ( TaggedField :: Features ( features) ) => {
10551073 if features. requires_unknown_bits ( ) {
10561074 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 ( ) {
1075+ } else if !features. supports_payment_secret ( ) {
10621076 Err ( SemanticError :: InvalidFeatures )
10631077 } else {
10641078 Ok ( ( ) )
@@ -1154,8 +1168,8 @@ impl Invoice {
11541168 }
11551169
11561170 /// 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 ( )
1171+ pub fn payment_secret ( & self ) -> & PaymentSecret {
1172+ self . signed_invoice . payment_secret ( ) . expect ( "was checked by constructor" )
11591173 }
11601174
11611175 /// Get the invoice features if they were included in the invoice
@@ -1412,6 +1426,10 @@ pub enum SemanticError {
14121426 /// The invoice contains multiple descriptions and/or description hashes which isn't allowed
14131427 MultipleDescriptions ,
14141428
1429+ /// The invoice is missing the mandatory payment secret, which all modern lightning nodes
1430+ /// should provide.
1431+ NoPaymentSecret ,
1432+
14151433 /// The invoice contains multiple payment secrets
14161434 MultiplePaymentSecrets ,
14171435
@@ -1435,6 +1453,7 @@ impl Display for SemanticError {
14351453 SemanticError :: MultiplePaymentHashes => f. write_str ( "The invoice has multiple payment hashes which isn't allowed" ) ,
14361454 SemanticError :: NoDescription => f. write_str ( "No description or description hash are part of the invoice" ) ,
14371455 SemanticError :: MultipleDescriptions => f. write_str ( "The invoice contains multiple descriptions and/or description hashes which isn't allowed" ) ,
1456+ SemanticError :: NoPaymentSecret => f. write_str ( "The invoice is missing the mandatory payment secret" ) ,
14381457 SemanticError :: MultiplePaymentSecrets => f. write_str ( "The invoice contains multiple payment secrets" ) ,
14391458 SemanticError :: InvalidFeatures => f. write_str ( "The invoice's features are invalid" ) ,
14401459 SemanticError :: InvalidRecoveryId => f. write_str ( "The recovery id doesn't fit the signature/pub key" ) ,
@@ -1651,23 +1670,23 @@ mod test {
16511670 let invoice = invoice_template. clone ( ) ;
16521671 invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
16531672 } . unwrap ( ) ;
1654- assert ! ( Invoice :: from_signed( invoice) . is_ok ( ) ) ;
1673+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
16551674
16561675 // No payment secret or feature bits
16571676 let invoice = {
16581677 let mut invoice = invoice_template. clone ( ) ;
16591678 invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: empty ( ) ) . into ( ) ) ;
16601679 invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
16611680 } . unwrap ( ) ;
1662- assert ! ( Invoice :: from_signed( invoice) . is_ok ( ) ) ;
1681+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
16631682
16641683 // Missing payment secret
16651684 let invoice = {
16661685 let mut invoice = invoice_template. clone ( ) ;
16671686 invoice. data . tagged_fields . push ( Features ( InvoiceFeatures :: known ( ) ) . into ( ) ) ;
16681687 invoice. sign :: < _ , ( ) > ( |hash| Ok ( Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key) ) )
16691688 } . unwrap ( ) ;
1670- assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: InvalidFeatures ) ) ;
1689+ assert_eq ! ( Invoice :: from_signed( invoice) , Err ( SemanticError :: NoPaymentSecret ) ) ;
16711690
16721691 // Multiple payment secrets
16731692 let invoice = {
@@ -1753,6 +1772,7 @@ mod test {
17531772
17541773 let sign_error_res = builder. clone ( )
17551774 . description ( "Test" . into ( ) )
1775+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
17561776 . try_build_signed ( |_| {
17571777 Err ( "ImaginaryError" )
17581778 } ) ;
@@ -1865,7 +1885,7 @@ mod test {
18651885 InvoiceDescription :: Hash ( & Sha256 ( sha256:: Hash :: from_slice( & [ 3 ; 32 ] [ ..] ) . unwrap( ) ) )
18661886 ) ;
18671887 assert_eq ! ( invoice. payment_hash( ) , & sha256:: Hash :: from_slice( & [ 21 ; 32 ] [ ..] ) . unwrap( ) ) ;
1868- assert_eq ! ( invoice. payment_secret( ) , Some ( & PaymentSecret ( [ 42 ; 32 ] ) ) ) ;
1888+ assert_eq ! ( invoice. payment_secret( ) , & PaymentSecret ( [ 42 ; 32 ] ) ) ;
18691889 assert_eq ! ( invoice. features( ) , Some ( & InvoiceFeatures :: known( ) ) ) ;
18701890
18711891 let raw_invoice = builder. build_raw ( ) . unwrap ( ) ;
@@ -1881,6 +1901,7 @@ mod test {
18811901 let signed_invoice = InvoiceBuilder :: new ( Currency :: Bitcoin )
18821902 . description ( "Test" . into ( ) )
18831903 . payment_hash ( sha256:: Hash :: from_slice ( & [ 0 ; 32 ] [ ..] ) . unwrap ( ) )
1904+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
18841905 . current_timestamp ( )
18851906 . build_raw ( )
18861907 . unwrap ( )
0 commit comments