@@ -209,9 +209,8 @@ impl MetadataStrategy for DerivedMetadata {}
209209macro_rules! offer_explicit_metadata_builder_methods { (
210210 $self: ident, $self_type: ty, $return_type: ty, $return_value: expr
211211) => {
212- /// Creates a new builder for an offer setting an empty [`Offer::description`] and using the
213- /// [`Offer::signing_pubkey`] for signing invoices. The associated secret key must be remembered
214- /// while the offer is valid.
212+ /// Creates a new builder for an offer using the [`Offer::signing_pubkey`] for signing invoices.
213+ /// The associated secret key must be remembered while the offer is valid.
215214 ///
216215 /// Use a different pubkey per offer to avoid correlating offers.
217216 ///
@@ -225,7 +224,7 @@ macro_rules! offer_explicit_metadata_builder_methods { (
225224 pub fn new( signing_pubkey: PublicKey ) -> Self {
226225 Self {
227226 offer: OfferContents {
228- chains: None , metadata: None , amount: None , description: String :: new ( ) ,
227+ chains: None , metadata: None , amount: None , description: None ,
229228 features: OfferFeatures :: empty( ) , absolute_expiry: None , issuer: None , paths: None ,
230229 supported_quantity: Quantity :: One , signing_pubkey: Some ( signing_pubkey) ,
231230 } ,
@@ -264,7 +263,7 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
264263 let metadata = Metadata :: DerivedSigningPubkey ( derivation_material) ;
265264 Self {
266265 offer: OfferContents {
267- chains: None , metadata: Some ( metadata) , amount: None , description: String :: new ( ) ,
266+ chains: None , metadata: Some ( metadata) , amount: None , description: None ,
268267 features: OfferFeatures :: empty( ) , absolute_expiry: None , issuer: None , paths: None ,
269268 supported_quantity: Quantity :: One , signing_pubkey: Some ( node_id) ,
270269 } ,
@@ -330,7 +329,7 @@ macro_rules! offer_builder_methods { (
330329 ///
331330 /// Successive calls to this method will override the previous setting.
332331 pub fn description( $( $self_mut) * $self: $self_type, description: String ) -> $return_type {
333- $self. offer. description = description;
332+ $self. offer. description = Some ( description) ;
334333 $return_value
335334 }
336335
@@ -373,6 +372,10 @@ macro_rules! offer_builder_methods { (
373372 None => { } ,
374373 }
375374
375+ if $self. offer. amount. is_some( ) && $self. offer. description. is_none( ) {
376+ $self. offer. description = Some ( String :: new( ) ) ;
377+ }
378+
376379 if let Some ( chains) = & $self. offer. chains {
377380 if chains. len( ) == 1 && chains[ 0 ] == $self. offer. implied_chain( ) {
378381 $self. offer. chains = None ;
@@ -551,7 +554,7 @@ pub(super) struct OfferContents {
551554 chains : Option < Vec < ChainHash > > ,
552555 metadata : Option < Metadata > ,
553556 amount : Option < Amount > ,
554- description : String ,
557+ description : Option < String > ,
555558 features : OfferFeatures ,
556559 absolute_expiry : Option < Duration > ,
557560 issuer : Option < String > ,
@@ -585,7 +588,7 @@ macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
585588
586589 /// A complete description of the purpose of the payment. Intended to be displayed to the user
587590 /// but with the caveat that it has not been verified in any way.
588- pub fn description( & $self) -> $crate:: util:: string:: PrintableString {
591+ pub fn description( & $self) -> Option < $crate:: util:: string:: PrintableString > {
589592 $contents. description( )
590593 }
591594
@@ -809,8 +812,8 @@ impl OfferContents {
809812 self . amount . as_ref ( )
810813 }
811814
812- pub fn description ( & self ) -> PrintableString {
813- PrintableString ( & self . description )
815+ pub fn description ( & self ) -> Option < PrintableString > {
816+ self . description . as_ref ( ) . map ( |description| PrintableString ( description ) )
814817 }
815818
816819 pub fn features ( & self ) -> & OfferFeatures {
@@ -954,7 +957,7 @@ impl OfferContents {
954957 metadata : self . metadata ( ) ,
955958 currency,
956959 amount,
957- description : Some ( & self . description ) ,
960+ description : self . description . as_ref ( ) ,
958961 features,
959962 absolute_expiry : self . absolute_expiry . map ( |duration| duration. as_secs ( ) ) ,
960963 paths : self . paths . as_ref ( ) ,
@@ -1092,10 +1095,9 @@ impl TryFrom<OfferTlvStream> for OfferContents {
10921095 ( Some ( iso4217_code) , Some ( amount) ) => Some ( Amount :: Currency { iso4217_code, amount } ) ,
10931096 } ;
10941097
1095- let description = match description {
1096- None => return Err ( Bolt12SemanticError :: MissingDescription ) ,
1097- Some ( description) => description,
1098- } ;
1098+ if amount. is_some ( ) && description. is_none ( ) {
1099+ return Err ( Bolt12SemanticError :: MissingDescription ) ;
1100+ }
10991101
11001102 let features = features. unwrap_or_else ( OfferFeatures :: empty) ;
11011103
@@ -1166,7 +1168,7 @@ mod tests {
11661168 assert ! ( offer. supports_chain( ChainHash :: using_genesis_block( Network :: Bitcoin ) ) ) ;
11671169 assert_eq ! ( offer. metadata( ) , None ) ;
11681170 assert_eq ! ( offer. amount( ) , None ) ;
1169- assert_eq ! ( offer. description( ) , PrintableString ( "" ) ) ;
1171+ assert_eq ! ( offer. description( ) , None ) ;
11701172 assert_eq ! ( offer. offer_features( ) , & OfferFeatures :: empty( ) ) ;
11711173 assert_eq ! ( offer. absolute_expiry( ) , None ) ;
11721174 #[ cfg( feature = "std" ) ]
@@ -1183,7 +1185,7 @@ mod tests {
11831185 metadata: None ,
11841186 currency: None ,
11851187 amount: None ,
1186- description: Some ( & String :: from ( "" ) ) ,
1188+ description: None ,
11871189 features: None ,
11881190 absolute_expiry: None ,
11891191 paths: None ,
@@ -1421,16 +1423,23 @@ mod tests {
14211423 . description ( "foo" . into ( ) )
14221424 . build ( )
14231425 . unwrap ( ) ;
1424- assert_eq ! ( offer. description( ) , PrintableString ( "foo" ) ) ;
1426+ assert_eq ! ( offer. description( ) , Some ( PrintableString ( "foo" ) ) ) ;
14251427 assert_eq ! ( offer. as_tlv_stream( ) . description, Some ( & String :: from( "foo" ) ) ) ;
14261428
14271429 let offer = OfferBuilder :: new ( pubkey ( 42 ) )
14281430 . description ( "foo" . into ( ) )
14291431 . description ( "bar" . into ( ) )
14301432 . build ( )
14311433 . unwrap ( ) ;
1432- assert_eq ! ( offer. description( ) , PrintableString ( "bar" ) ) ;
1434+ assert_eq ! ( offer. description( ) , Some ( PrintableString ( "bar" ) ) ) ;
14331435 assert_eq ! ( offer. as_tlv_stream( ) . description, Some ( & String :: from( "bar" ) ) ) ;
1436+
1437+ let offer = OfferBuilder :: new ( pubkey ( 42 ) )
1438+ . amount_msats ( 1000 )
1439+ . build ( )
1440+ . unwrap ( ) ;
1441+ assert_eq ! ( offer. description( ) , Some ( PrintableString ( "" ) ) ) ;
1442+ assert_eq ! ( offer. as_tlv_stream( ) . description, Some ( & String :: from( "" ) ) ) ;
14341443 }
14351444
14361445 #[ test]
@@ -1655,6 +1664,14 @@ mod tests {
16551664 panic ! ( "error parsing offer: {:?}" , e) ;
16561665 }
16571666
1667+ let offer = OfferBuilder :: new ( pubkey ( 42 ) )
1668+ . description ( "foo" . to_string ( ) )
1669+ . amount_msats ( 1000 )
1670+ . build ( ) . unwrap ( ) ;
1671+ if let Err ( e) = offer. to_string ( ) . parse :: < Offer > ( ) {
1672+ panic ! ( "error parsing offer: {:?}" , e) ;
1673+ }
1674+
16581675 let mut tlv_stream = offer. as_tlv_stream ( ) ;
16591676 tlv_stream. description = None ;
16601677
@@ -1875,7 +1892,7 @@ mod bolt12_tests {
18751892 // Malformed: empty
18761893 assert_eq ! (
18771894 "lno1" . parse:: <Offer >( ) ,
1878- Err ( Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingDescription ) ) ,
1895+ Err ( Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSigningPubkey ) ) ,
18791896 ) ;
18801897
18811898 // Malformed: truncated at type
@@ -2000,7 +2017,8 @@ mod bolt12_tests {
20002017
20012018 // Missing offer_description
20022019 assert_eq ! (
2003- "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese" . parse:: <Offer >( ) ,
2020+ // TODO: Match the spec once it is updated.
2021+ "lno1pqpq86qkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg" . parse:: <Offer >( ) ,
20042022 Err ( Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingDescription ) ) ,
20052023 ) ;
20062024
0 commit comments