@@ -44,7 +44,16 @@ use crate::sign::EntropySource;
4444use crate :: util:: logger:: { Logger , WithContext } ;
4545
4646#[ cfg( async_payments) ]
47- use crate :: offers:: static_invoice:: StaticInvoice ;
47+ use {
48+ crate :: blinded_path:: message:: AsyncPaymentsContext ,
49+ crate :: blinded_path:: payment:: AsyncBolt12OfferContext ,
50+ crate :: offers:: offer:: { Amount , Offer } ,
51+ crate :: offers:: signer,
52+ crate :: offers:: static_invoice:: {
53+ StaticInvoice , StaticInvoiceBuilder ,
54+ DEFAULT_RELATIVE_EXPIRY as STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY ,
55+ } ,
56+ } ;
4857
4958#[ cfg( not( c_bindings) ) ]
5059use crate :: offers:: offer:: DerivedMetadata ;
@@ -181,6 +190,10 @@ pub trait OffersMessageCommons {
181190 max_total_routing_fee_msat : Option < u64 > ,
182191 retryable_invoice_request : Option < RetryableInvoiceRequest > ,
183192 ) -> Result < ( ) , ( ) > ;
193+
194+ #[ cfg( not( feature = "std" ) ) ]
195+ /// Get the approximate current time using the highest seen timestamp
196+ fn get_highest_seen_timestamp ( & self ) -> Duration ;
184197}
185198
186199/// A trivial trait which describes any [`OffersMessageFlow`].
@@ -476,6 +489,24 @@ where
476489 }
477490}
478491
492+ impl < ES : Deref , OMC : Deref , L : Deref > OffersMessageFlow < ES , OMC , L >
493+ where
494+ ES :: Target : EntropySource ,
495+ OMC :: Target : OffersMessageCommons ,
496+ L :: Target : Logger ,
497+ {
498+ pub ( crate ) fn duration_since_epoch ( & self ) -> Duration {
499+ #[ cfg( not( feature = "std" ) ) ]
500+ let now = self . commons . get_highest_seen_timestamp ( ) ;
501+ #[ cfg( feature = "std" ) ]
502+ let now = std:: time:: SystemTime :: now ( )
503+ . duration_since ( std:: time:: SystemTime :: UNIX_EPOCH )
504+ . expect ( "SystemTime::now() should come after SystemTime::UNIX_EPOCH" ) ;
505+
506+ now
507+ }
508+ }
509+
479510impl < ES : Deref , OMC : Deref , L : Deref > OffersMessageHandler for OffersMessageFlow < ES , OMC , L >
480511where
481512 ES :: Target : EntropySource ,
@@ -955,4 +986,68 @@ where
955986
956987 Ok ( ( builder. into ( ) , nonce) )
957988 }
989+
990+ /// Creates a [`StaticInvoiceBuilder`] from the corresponding [`Offer`] and [`Nonce`] that were
991+ /// created via [`create_async_receive_offer_builder`]. If `relative_expiry` is unset, the
992+ /// invoice's expiry will default to [`STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY`].
993+ ///
994+ /// [`create_async_receive_offer_builder`]: crate::offers::flow::create_async_receive_offer_builder
995+ #[ cfg( async_payments) ]
996+ pub fn create_static_invoice_builder < ' a > (
997+ & self , offer : & ' a Offer , offer_nonce : Nonce , relative_expiry : Option < Duration > ,
998+ ) -> Result < StaticInvoiceBuilder < ' a > , Bolt12SemanticError > {
999+ let expanded_key = & self . inbound_payment_key ;
1000+ let entropy = & * self . entropy_source ;
1001+ let secp_ctx = & self . secp_ctx ;
1002+
1003+ let payment_context =
1004+ PaymentContext :: AsyncBolt12Offer ( AsyncBolt12OfferContext { offer_nonce } ) ;
1005+ let amount_msat = offer. amount ( ) . and_then ( |amount| match amount {
1006+ Amount :: Bitcoin { amount_msats } => Some ( amount_msats) ,
1007+ Amount :: Currency { .. } => None ,
1008+ } ) ;
1009+
1010+ let relative_expiry = relative_expiry. unwrap_or ( STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY ) ;
1011+ let relative_expiry_secs: u32 = relative_expiry. as_secs ( ) . try_into ( ) . unwrap_or ( u32:: MAX ) ;
1012+
1013+ let created_at = self . duration_since_epoch ( ) ;
1014+ let payment_secret = inbound_payment:: create_for_spontaneous_payment (
1015+ & self . inbound_payment_key ,
1016+ amount_msat,
1017+ relative_expiry_secs,
1018+ created_at. as_secs ( ) ,
1019+ None ,
1020+ )
1021+ . map_err ( |( ) | Bolt12SemanticError :: InvalidAmount ) ?;
1022+
1023+ let payment_paths = self
1024+ . commons
1025+ . create_blinded_payment_paths (
1026+ amount_msat,
1027+ payment_secret,
1028+ payment_context,
1029+ relative_expiry_secs,
1030+ )
1031+ . map_err ( |( ) | Bolt12SemanticError :: MissingPaths ) ?;
1032+
1033+ let nonce = Nonce :: from_entropy_source ( entropy) ;
1034+ let hmac = signer:: hmac_for_held_htlc_available_context ( nonce, expanded_key) ;
1035+ let context =
1036+ MessageContext :: AsyncPayments ( AsyncPaymentsContext :: InboundPayment { nonce, hmac } ) ;
1037+ let async_receive_message_paths = self
1038+ . commons
1039+ . create_blinded_paths ( context)
1040+ . map_err ( |( ) | Bolt12SemanticError :: MissingPaths ) ?;
1041+
1042+ StaticInvoiceBuilder :: for_offer_using_derived_keys (
1043+ offer,
1044+ payment_paths,
1045+ async_receive_message_paths,
1046+ created_at,
1047+ expanded_key,
1048+ offer_nonce,
1049+ secp_ctx,
1050+ )
1051+ . map ( |inv| inv. allow_mpp ( ) . relative_expiry ( relative_expiry_secs) )
1052+ }
9581053}
0 commit comments