@@ -54,21 +54,38 @@ impl Readable for BlindedMessagePath {
5454
5555impl BlindedMessagePath {
5656 /// Create a one-hop blinded path for a message.
57+ ///
58+ /// `compact_padding` selects between space-inefficient padding which better hides contents and
59+ /// a space-constrained padding which does very little to hide the contents, especially for the
60+ /// last hop. It should only be set when the blinded path needs to be as compact as possible.
5761 pub fn one_hop < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
5862 recipient_node_id : PublicKey , local_node_receive_key : ReceiveAuthKey ,
59- context : MessageContext , entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
63+ context : MessageContext , compact_padding : bool , entropy_source : ES ,
64+ secp_ctx : & Secp256k1 < T > ,
6065 ) -> Self
6166 where
6267 ES :: Target : EntropySource ,
6368 {
64- Self :: new ( & [ ] , recipient_node_id, local_node_receive_key, context, entropy_source, secp_ctx)
69+ Self :: new (
70+ & [ ] ,
71+ recipient_node_id,
72+ local_node_receive_key,
73+ context,
74+ compact_padding,
75+ entropy_source,
76+ secp_ctx,
77+ )
6578 }
6679
6780 /// Create a path for an onion message, to be forwarded along `node_pks`.
81+ ///
82+ /// `compact_padding` selects between space-inefficient padding which better hides contents and
83+ /// a space-constrained padding which does very little to hide the contents, especially for the
84+ /// last hop. It should only be set when the blinded path needs to be as compact as possible.
6885 pub fn new < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
6986 intermediate_nodes : & [ MessageForwardNode ] , recipient_node_id : PublicKey ,
70- local_node_receive_key : ReceiveAuthKey , context : MessageContext , entropy_source : ES ,
71- secp_ctx : & Secp256k1 < T > ,
87+ local_node_receive_key : ReceiveAuthKey , context : MessageContext , compact_padding : bool ,
88+ entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
7289 ) -> Self
7390 where
7491 ES :: Target : EntropySource ,
@@ -79,19 +96,23 @@ impl BlindedMessagePath {
7996 0 ,
8097 local_node_receive_key,
8198 context,
99+ compact_padding,
82100 entropy_source,
83101 secp_ctx,
84102 )
85103 }
86104
87105 /// Same as [`BlindedMessagePath::new`], but allows specifying a number of dummy hops.
88106 ///
89- /// Note:
90- /// At most [`MAX_DUMMY_HOPS_COUNT`] dummy hops can be added to the blinded path.
107+ /// `compact_padding` selects between space-inefficient padding which better hides contents and
108+ /// a space-constrained padding which does very little to hide the contents, especially for the
109+ /// last hop. It should only be set when the blinded path needs to be as compact as possible.
110+ ///
111+ /// Note: At most [`MAX_DUMMY_HOPS_COUNT`] dummy hops can be added to the blinded path.
91112 pub fn new_with_dummy_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
92113 intermediate_nodes : & [ MessageForwardNode ] , recipient_node_id : PublicKey ,
93114 dummy_hop_count : usize , local_node_receive_key : ReceiveAuthKey , context : MessageContext ,
94- entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
115+ compact_padding : bool , entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
95116 ) -> Self
96117 where
97118 ES :: Target : EntropySource ,
@@ -114,6 +135,7 @@ impl BlindedMessagePath {
114135 context,
115136 & blinding_secret,
116137 local_node_receive_key,
138+ compact_padding,
117139 ) ,
118140 } )
119141 }
@@ -416,28 +438,45 @@ pub enum OffersContext {
416438 /// Useful to timeout async recipients that are no longer supported as clients.
417439 path_absolute_expiry : Duration ,
418440 } ,
419- /// Context used by a [`BlindedMessagePath`] within a [`Refund`] or as a reply path for an
420- /// [`InvoiceRequest`].
441+ /// Context used by a [`BlindedMessagePath`] within a [`Refund`].
421442 ///
422443 /// This variant is intended to be received when handling a [`Bolt12Invoice`] or an
423444 /// [`InvoiceError`].
424445 ///
425446 /// [`Refund`]: crate::offers::refund::Refund
426- /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
427447 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
428448 /// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
429- OutboundPayment {
430- /// Payment ID used when creating a [`Refund`] or [`InvoiceRequest`] .
449+ OutboundPaymentForRefund {
450+ /// Payment ID used when creating a [`Refund`].
431451 ///
432452 /// [`Refund`]: crate::offers::refund::Refund
433- /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
434453 payment_id : PaymentId ,
435454
436- /// A nonce used for authenticating that a [`Bolt12Invoice`] is for a valid [`Refund`] or
437- /// [`InvoiceRequest`] and for deriving their signing keys.
455+ /// A nonce used for authenticating that a [`Bolt12Invoice`] is for a valid [`Refund`] and
456+ /// for deriving its signing keys.
438457 ///
439458 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
440459 /// [`Refund`]: crate::offers::refund::Refund
460+ nonce : Nonce ,
461+ } ,
462+ /// Context used by a [`BlindedMessagePath`] as a reply path for an [`InvoiceRequest`].
463+ ///
464+ /// This variant is intended to be received when handling a [`Bolt12Invoice`] or an
465+ /// [`InvoiceError`].
466+ ///
467+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
468+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
469+ /// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
470+ OutboundPaymentForOffer {
471+ /// Payment ID used when creating an [`InvoiceRequest`].
472+ ///
473+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
474+ payment_id : PaymentId ,
475+
476+ /// A nonce used for authenticating that a [`Bolt12Invoice`] is for a valid
477+ /// [`InvoiceRequest`] and for deriving its signing keys.
478+ ///
479+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
441480 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
442481 nonce : Nonce ,
443482 } ,
@@ -619,7 +658,7 @@ impl_writeable_tlv_based_enum!(OffersContext,
619658 ( 0 , InvoiceRequest ) => {
620659 ( 0 , nonce, required) ,
621660 } ,
622- ( 1 , OutboundPayment ) => {
661+ ( 1 , OutboundPaymentForRefund ) => {
623662 ( 0 , payment_id, required) ,
624663 ( 1 , nonce, required) ,
625664 } ,
@@ -631,6 +670,10 @@ impl_writeable_tlv_based_enum!(OffersContext,
631670 ( 2 , invoice_slot, required) ,
632671 ( 4 , path_absolute_expiry, required) ,
633672 } ,
673+ ( 4 , OutboundPaymentForOffer ) => {
674+ ( 0 , payment_id, required) ,
675+ ( 1 , nonce, required) ,
676+ } ,
634677) ;
635678
636679impl_writeable_tlv_based_enum ! ( AsyncPaymentsContext ,
@@ -693,7 +736,7 @@ pub const MAX_DUMMY_HOPS_COUNT: usize = 10;
693736pub ( super ) fn blinded_hops < T : secp256k1:: Signing + secp256k1:: Verification > (
694737 secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ MessageForwardNode ] ,
695738 recipient_node_id : PublicKey , dummy_hop_count : usize , context : MessageContext ,
696- session_priv : & SecretKey , local_node_receive_key : ReceiveAuthKey ,
739+ session_priv : & SecretKey , local_node_receive_key : ReceiveAuthKey , compact_padding : bool ,
697740) -> Vec < BlindedHop > {
698741 let dummy_count = cmp:: min ( dummy_hop_count, MAX_DUMMY_HOPS_COUNT ) ;
699742 let pks = intermediate_nodes
@@ -703,9 +746,8 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
703746 core:: iter:: repeat ( ( recipient_node_id, Some ( local_node_receive_key) ) ) . take ( dummy_count) ,
704747 )
705748 . chain ( core:: iter:: once ( ( recipient_node_id, Some ( local_node_receive_key) ) ) ) ;
706- let is_compact = intermediate_nodes. iter ( ) . any ( |node| node. short_channel_id . is_some ( ) ) ;
707749
708- let tlvs = pks
750+ let intermediate_tlvs = pks
709751 . clone ( )
710752 . skip ( 1 ) // The first node's TLVs contains the next node's pubkey
711753 . zip ( intermediate_nodes. iter ( ) . map ( |node| node. short_channel_id ) )
@@ -716,18 +758,43 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
716758 . map ( |next_hop| {
717759 ControlTlvs :: Forward ( ForwardTlvs { next_hop, next_blinding_override : None } )
718760 } )
719- . chain ( ( 0 ..dummy_count) . map ( |_| ControlTlvs :: Dummy ) )
720- . chain ( core:: iter:: once ( ControlTlvs :: Receive ( ReceiveTlvs { context : Some ( context) } ) ) ) ;
721-
722- if is_compact {
723- let path = pks. zip ( tlvs) ;
724- utils:: construct_blinded_hops ( secp_ctx, path, session_priv)
761+ . chain ( ( 0 ..dummy_count) . map ( |_| ControlTlvs :: Dummy ) ) ;
762+
763+ let max_intermediate_len =
764+ intermediate_tlvs. clone ( ) . map ( |tlvs| tlvs. serialized_length ( ) ) . max ( ) . unwrap_or ( 0 ) ;
765+ let have_intermediate_one_byte_smaller =
766+ intermediate_tlvs. clone ( ) . any ( |tlvs| tlvs. serialized_length ( ) == max_intermediate_len - 1 ) ;
767+
768+ let round_off = if compact_padding {
769+ // We can only pad by a minimum of two bytes (we can only go from no-TLV to a type + length
770+ // byte). Thus, if there are any intermediate hops that need to be padded by exactly one
771+ // byte, we have to instead pad everything by two.
772+ if have_intermediate_one_byte_smaller {
773+ max_intermediate_len + 2
774+ } else {
775+ max_intermediate_len
776+ }
725777 } else {
726- let path =
727- pks. zip ( tlvs. map ( |tlv| BlindedPathWithPadding {
728- tlvs : tlv,
729- round_off : MESSAGE_PADDING_ROUND_OFF ,
730- } ) ) ;
731- utils:: construct_blinded_hops ( secp_ctx, path, session_priv)
732- }
778+ MESSAGE_PADDING_ROUND_OFF
779+ } ;
780+
781+ let tlvs = intermediate_tlvs
782+ . map ( |tlvs| {
783+ let res = BlindedPathWithPadding { tlvs, round_off } ;
784+ if compact_padding {
785+ debug_assert_eq ! ( res. serialized_length( ) , max_intermediate_len) ;
786+ } else {
787+ // We don't currently ever push extra fields to intermediate hops, so they should
788+ // never go over `MESSAGE_PADDING_ROUND_OFF`.
789+ debug_assert_eq ! ( res. serialized_length( ) , MESSAGE_PADDING_ROUND_OFF ) ;
790+ }
791+ res
792+ } )
793+ . chain ( core:: iter:: once ( BlindedPathWithPadding {
794+ tlvs : ControlTlvs :: Receive ( ReceiveTlvs { context : Some ( context) } ) ,
795+ round_off : if compact_padding { 0 } else { MESSAGE_PADDING_ROUND_OFF } ,
796+ } ) ) ;
797+
798+ let path = pks. zip ( tlvs) ;
799+ utils:: construct_blinded_hops ( secp_ctx, path, session_priv)
733800}
0 commit comments