@@ -419,7 +419,8 @@ macro_rules! invoice_builder_methods {
419419 pub ( crate ) fn recurrence_fields(
420420 _invoice_request: & InvoiceRequest , _recurrence_basetime: Option <u64 >,
421421 ) -> Result <Option <InvoiceRecurrence >, Bolt12SemanticError > {
422- todo!( "Future commits will introduce the Recurrence Token creation logic" )
422+ //TODO: Future commits will introduce the recurrence token creation logic
423+ return Ok ( None )
423424 }
424425
425426 #[ cfg_attr( c_bindings, allow( dead_code) ) ]
@@ -1880,8 +1881,9 @@ pub(super) fn check_invoice_signing_pubkey(
18801881mod tests {
18811882 use super :: {
18821883 Bolt12Invoice , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef ,
1883- InvoiceTlvStreamRef , UnsignedBolt12Invoice , DEFAULT_RELATIVE_EXPIRY ,
1884- EXPERIMENTAL_INVOICE_TYPES , INVOICE_TYPES , SIGNATURE_TAG ,
1884+ InvoiceContents , InvoiceFields , InvoiceRecurrence , InvoiceTlvStreamRef ,
1885+ UnsignedBolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , INVOICE_TYPES ,
1886+ SIGNATURE_TAG ,
18851887 } ;
18861888
18871889 use bitcoin:: address:: Address ;
@@ -3452,6 +3454,163 @@ mod tests {
34523454 }
34533455 }
34543456
3457+ #[ test]
3458+ fn parses_invoice_with_recurrence ( ) {
3459+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3460+ let entropy = FixedEntropy { } ;
3461+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3462+ let secp_ctx = Secp256k1 :: new ( ) ;
3463+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3464+ let recurrence_basetime = 123_456 ;
3465+ let recurrence_token = vec ! [ 1 , 2 , 3 ] ;
3466+
3467+ let offer = OfferBuilder :: new ( recipient_pubkey ( ) ) . amount_msats ( 1000 ) . build ( ) . unwrap ( ) ;
3468+ let invoice_request = offer
3469+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3470+ . unwrap ( )
3471+ . build_and_sign ( )
3472+ . unwrap ( ) ;
3473+
3474+ let contents = InvoiceContents :: ForOffer {
3475+ invoice_request : invoice_request. contents . clone ( ) ,
3476+ fields : InvoiceFields {
3477+ payment_paths : payment_paths ( ) ,
3478+ created_at : now ( ) ,
3479+ relative_expiry : None ,
3480+ payment_hash : payment_hash ( ) ,
3481+ amount_msats : 1000 ,
3482+ fallbacks : None ,
3483+ features : Bolt12InvoiceFeatures :: empty ( ) ,
3484+ signing_pubkey : recipient_pubkey ( ) ,
3485+ #[ cfg( test) ]
3486+ experimental_baz : None ,
3487+ invoice_recurrence : Some ( InvoiceRecurrence {
3488+ recurrence_basetime,
3489+ recurrence_token : recurrence_token. clone ( ) ,
3490+ } ) ,
3491+ } ,
3492+ } ;
3493+
3494+ let invoice = UnsignedBolt12Invoice :: new ( invoice_request. bytes ( ) , contents)
3495+ . sign ( recipient_sign)
3496+ . unwrap ( ) ;
3497+
3498+ let mut buffer = Vec :: new ( ) ;
3499+ invoice. write ( & mut buffer) . unwrap ( ) ;
3500+
3501+ match Bolt12Invoice :: try_from ( buffer) {
3502+ Ok ( invoice) => {
3503+ match & invoice. contents {
3504+ InvoiceContents :: ForOffer { fields, .. } => {
3505+ assert_eq ! (
3506+ fields. invoice_recurrence,
3507+ Some ( InvoiceRecurrence {
3508+ recurrence_basetime,
3509+ recurrence_token: recurrence_token. clone( ) ,
3510+ } )
3511+ ) ;
3512+ } ,
3513+ InvoiceContents :: ForRefund { .. } => panic ! ( "expected offer invoice" ) ,
3514+ }
3515+
3516+ let tlv_stream = invoice. as_tlv_stream ( ) ;
3517+ assert_eq ! ( tlv_stream. 3 . invoice_recurrence_basetime, Some ( recurrence_basetime) ) ;
3518+ assert_eq ! ( tlv_stream. 3 . invoice_recurrence_token, Some ( & recurrence_token) ) ;
3519+ } ,
3520+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
3521+ }
3522+ }
3523+
3524+ #[ test]
3525+ fn fails_parsing_invoice_with_invalid_recurrence ( ) {
3526+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3527+ let entropy = FixedEntropy { } ;
3528+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3529+ let secp_ctx = Secp256k1 :: new ( ) ;
3530+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3531+ let recurrence_token = vec ! [ 1 , 2 , 3 ] ;
3532+
3533+ let offer = OfferBuilder :: new ( recipient_pubkey ( ) ) . amount_msats ( 1000 ) . build ( ) . unwrap ( ) ;
3534+ let invoice_request = offer
3535+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3536+ . unwrap ( )
3537+ . build_and_sign ( )
3538+ . unwrap ( ) ;
3539+
3540+ let contents = InvoiceContents :: ForOffer {
3541+ invoice_request : invoice_request. contents . clone ( ) ,
3542+ fields : InvoiceFields {
3543+ payment_paths : payment_paths ( ) ,
3544+ created_at : now ( ) ,
3545+ relative_expiry : None ,
3546+ payment_hash : payment_hash ( ) ,
3547+ amount_msats : 1000 ,
3548+ fallbacks : None ,
3549+ features : Bolt12InvoiceFeatures :: empty ( ) ,
3550+ signing_pubkey : recipient_pubkey ( ) ,
3551+ #[ cfg( test) ]
3552+ experimental_baz : None ,
3553+ invoice_recurrence : Some ( InvoiceRecurrence {
3554+ recurrence_basetime : 123_456 ,
3555+ recurrence_token : recurrence_token. clone ( ) ,
3556+ } ) ,
3557+ } ,
3558+ } ;
3559+
3560+ let invoice = UnsignedBolt12Invoice :: new ( invoice_request. bytes ( ) , contents)
3561+ . sign ( recipient_sign)
3562+ . unwrap ( ) ;
3563+
3564+ let mut missing_token = invoice. as_tlv_stream ( ) ;
3565+ missing_token. 3 . invoice_recurrence_token = None ;
3566+
3567+ match Bolt12Invoice :: try_from ( missing_token. to_bytes ( ) ) {
3568+ Ok ( _) => panic ! ( "expected error" ) ,
3569+ Err ( e) => assert_eq ! (
3570+ e,
3571+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: InvalidRecurrence )
3572+ ) ,
3573+ }
3574+
3575+ let mut missing_basetime = invoice. as_tlv_stream ( ) ;
3576+ missing_basetime. 3 . invoice_recurrence_basetime = None ;
3577+
3578+ match Bolt12Invoice :: try_from ( missing_basetime. to_bytes ( ) ) {
3579+ Ok ( _) => panic ! ( "expected error" ) ,
3580+ Err ( e) => assert_eq ! (
3581+ e,
3582+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: InvalidRecurrence )
3583+ ) ,
3584+ }
3585+ }
3586+
3587+ #[ test]
3588+ fn fails_parsing_refund_invoice_with_recurrence ( ) {
3589+ let refund =
3590+ RefundBuilder :: new ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( ) . build ( ) . unwrap ( ) ;
3591+
3592+ let invoice = refund
3593+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
3594+ . unwrap ( )
3595+ . build ( )
3596+ . unwrap ( )
3597+ . sign ( recipient_sign)
3598+ . unwrap ( ) ;
3599+
3600+ let recurrence_token = vec ! [ 1 , 2 , 3 ] ;
3601+ let mut tlv_stream = invoice. as_tlv_stream ( ) ;
3602+ tlv_stream. 3 . invoice_recurrence_basetime = Some ( 123_456 ) ;
3603+ tlv_stream. 3 . invoice_recurrence_token = Some ( & recurrence_token) ;
3604+
3605+ match Bolt12Invoice :: try_from ( tlv_stream. to_bytes ( ) ) {
3606+ Ok ( _) => panic ! ( "expected error" ) ,
3607+ Err ( e) => assert_eq ! (
3608+ e,
3609+ Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: UnexpectedRecurrence )
3610+ ) ,
3611+ }
3612+ }
3613+
34553614 #[ test]
34563615 fn parses_invoice_with_experimental_tlv_records ( ) {
34573616 let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
0 commit comments