@@ -50,7 +50,7 @@ use core::time::Duration;
5050use crate :: blinded_path:: IntroductionNode ;
5151use crate :: blinded_path:: message:: BlindedMessagePath ;
5252use crate :: blinded_path:: payment:: { Bolt12OfferContext , Bolt12RefundContext , DummyTlvs , PaymentContext } ;
53- use crate :: blinded_path:: message:: OffersContext ;
53+ use crate :: blinded_path:: message:: { MessageContext , OffersContext } ;
5454use crate :: events:: { ClosureReason , Event , HTLCHandlingFailureType , PaidBolt12Invoice , PaymentFailureReason , PaymentPurpose } ;
5555use crate :: ln:: channelmanager:: { PaymentId , RecentPaymentDetails , self } ;
5656use crate :: ln:: outbound_payment:: { Bolt12PaymentError , RecipientOnionFields , Retry } ;
@@ -62,8 +62,9 @@ use crate::offers::invoice::Bolt12Invoice;
6262use crate :: offers:: invoice_error:: InvoiceError ;
6363use crate :: offers:: invoice_request:: { InvoiceRequest , InvoiceRequestFields , InvoiceRequestVerifiedFromOffer } ;
6464use crate :: offers:: nonce:: Nonce ;
65+ use crate :: offers:: offer:: OfferBuilder ;
6566use crate :: offers:: parse:: Bolt12SemanticError ;
66- use crate :: onion_message:: messenger:: { DefaultMessageRouter , Destination , MessageSendInstructions , NodeIdMessageRouter , NullMessageRouter , PeeledOnion , DUMMY_HOPS_PATH_LENGTH , QR_CODED_DUMMY_HOPS_PATH_LENGTH } ;
67+ use crate :: onion_message:: messenger:: { DefaultMessageRouter , Destination , MessageRouter , MessageSendInstructions , NodeIdMessageRouter , NullMessageRouter , PeeledOnion , DUMMY_HOPS_PATH_LENGTH , QR_CODED_DUMMY_HOPS_PATH_LENGTH } ;
6768use crate :: onion_message:: offers:: OffersMessage ;
6869use crate :: routing:: gossip:: { NodeAlias , NodeId } ;
6970use crate :: routing:: router:: { DEFAULT_PAYMENT_DUMMY_HOPS , PaymentParameters , RouteParameters , RouteParametersConfig } ;
@@ -258,7 +259,7 @@ fn claim_bolt12_payment_with_extra_fees<'a, 'b, 'c>(
258259
259260fn extract_offer_nonce < ' a , ' b , ' c > ( node : & Node < ' a , ' b , ' c > , message : & OnionMessage ) -> Nonce {
260261 match node. onion_messenger . peel_onion_message ( message) {
261- Ok ( PeeledOnion :: Offers ( _, Some ( OffersContext :: InvoiceRequest { nonce } ) , _) ) => nonce,
262+ Ok ( PeeledOnion :: Offers ( _, Some ( OffersContext :: InvoiceRequest { nonce, payment_metadata : _ } ) , _) ) => nonce,
262263 Ok ( PeeledOnion :: Offers ( _, context, _) ) => panic ! ( "Unexpected onion message context: {:?}" , context) ,
263264 Ok ( PeeledOnion :: Forward ( _, _) ) => panic ! ( "Unexpected onion message forward" ) ,
264265 Ok ( _) => panic ! ( "Unexpected onion message" ) ,
@@ -983,6 +984,89 @@ fn router_modifies_payment_metadata_in_blinded_path() {
983984 expect_recent_payment ! ( bob, RecentPaymentDetails :: Fulfilled , payment_id) ;
984985}
985986
987+ /// Checks that `payment_metadata` set in the [`OffersContext::InvoiceRequest`] of an offer's
988+ /// blinded message path is propagated to the [`Bolt12OfferContext`] in the resulting invoice's
989+ /// blinded payment paths and surfaced via [`Event::PaymentClaimable`] when the payment is received.
990+ #[ test]
991+ fn pays_for_offer_with_payment_metadata_in_invoice_request_context ( ) {
992+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
993+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
994+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
995+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
996+
997+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 1_000_000_000 ) ;
998+
999+ let alice = & nodes[ 0 ] ;
1000+ let alice_id = alice. node . get_our_node_id ( ) ;
1001+ let bob = & nodes[ 1 ] ;
1002+ let bob_id = bob. node . get_our_node_id ( ) ;
1003+
1004+ // Manually build an offer whose blinded message path carries `payment_metadata` in its
1005+ // `OffersContext::InvoiceRequest` context. The HEAD commit causes Alice's `ChannelManager` to
1006+ // copy this metadata onto the `Bolt12OfferContext` when she handles the inbound invoice
1007+ // request, embedding it in the invoice's blinded payment paths.
1008+ let mut expected_metadata = BTreeMap :: new ( ) ;
1009+ expected_metadata. insert ( 0u64 , vec ! [ 1 , 2 , 3 , 4 ] ) ;
1010+ expected_metadata. insert ( 7u64 , vec ! [ 0xab , 0xcd ] ) ;
1011+
1012+ let secp_ctx = Secp256k1 :: new ( ) ;
1013+ let nonce = Nonce :: from_entropy_source ( alice. keys_manager ) ;
1014+ let context = MessageContext :: Offers ( OffersContext :: InvoiceRequest {
1015+ nonce,
1016+ payment_metadata : Some ( expected_metadata. clone ( ) ) ,
1017+ } ) ;
1018+ let paths = alice. message_router . create_blinded_paths (
1019+ alice_id,
1020+ alice. keys_manager . get_receive_auth_key ( ) ,
1021+ context,
1022+ alice. node . test_get_peers_for_blinded_path ( ) ,
1023+ & secp_ctx,
1024+ ) . unwrap ( ) ;
1025+ assert ! ( !paths. is_empty( ) ) ;
1026+
1027+ let expanded_key = alice. keys_manager . get_expanded_key ( ) ;
1028+ let mut builder = OfferBuilder :: deriving_signing_pubkey ( alice_id, & expanded_key, nonce, & secp_ctx)
1029+ . chain ( Network :: Testnet )
1030+ . amount_msats ( 10_000_000 ) ;
1031+ for path in paths {
1032+ builder = builder. path ( path) ;
1033+ }
1034+ let offer = builder. build ( ) . unwrap ( ) ;
1035+
1036+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1037+ bob. node . pay_for_offer ( & offer, None , payment_id, Default :: default ( ) ) . unwrap ( ) ;
1038+ expect_recent_payment ! ( bob, RecentPaymentDetails :: AwaitingInvoice , payment_id) ;
1039+
1040+ let onion_message = bob. onion_messenger . next_onion_message_for_peer ( alice_id) . unwrap ( ) ;
1041+ alice. onion_messenger . handle_onion_message ( bob_id, & onion_message) ;
1042+
1043+ let ( invoice_request, _) = extract_invoice_request ( alice, & onion_message) ;
1044+
1045+ let onion_message = alice. onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
1046+ bob. onion_messenger . handle_onion_message ( alice_id, & onion_message) ;
1047+
1048+ let ( invoice, _) = extract_invoice ( bob, & onion_message) ;
1049+
1050+ let payment_context = PaymentContext :: Bolt12Offer ( Bolt12OfferContext {
1051+ offer_id : offer. id ( ) ,
1052+ invoice_request : InvoiceRequestFields {
1053+ payer_signing_pubkey : invoice_request. payer_signing_pubkey ( ) ,
1054+ quantity : None ,
1055+ payer_note_truncated : None ,
1056+ human_readable_name : None ,
1057+ } ,
1058+ payment_metadata : Some ( expected_metadata) ,
1059+ } ) ;
1060+
1061+ route_bolt12_payment ( bob, & [ alice] , & invoice) ;
1062+ expect_recent_payment ! ( bob, RecentPaymentDetails :: Pending , payment_id) ;
1063+
1064+ // `claim_bolt12_payment` asserts the surfaced `PaymentContext` matches `payment_context`
1065+ // above, including the embedded `payment_metadata`.
1066+ claim_bolt12_payment ( bob, & [ alice] , payment_context, & invoice) ;
1067+ expect_recent_payment ! ( bob, RecentPaymentDetails :: Fulfilled , payment_id) ;
1068+ }
1069+
9861070/// Checks that a refund can be paid through a one-hop blinded path and that ephemeral pubkeys are
9871071/// used rather than exposing a node's pubkey. However, the node's pubkey is still used as the
9881072/// introduction node of the blinded path.
0 commit comments