@@ -31,9 +31,9 @@ use crate::types::payment::PaymentHash;
3131use crate :: util:: scid_utils;
3232use crate :: util:: ser:: { FixedLengthReader , LengthReadableArgs , Readable , Writeable , Writer } ;
3333
34- use core:: mem;
3534use core:: ops:: Deref ;
3635use core:: time:: Duration ;
36+ use core:: { cmp, mem} ;
3737
3838/// A blinded path to be used for sending or receiving a message, hiding the identity of the
3939/// recipient.
@@ -74,6 +74,29 @@ impl BlindedMessagePath {
7474 local_node_receive_key : ReceiveAuthKey , context : MessageContext , entropy_source : ES ,
7575 secp_ctx : & Secp256k1 < T > ,
7676 ) -> Result < Self , ( ) >
77+ where
78+ ES :: Target : EntropySource ,
79+ {
80+ BlindedMessagePath :: new_with_dummy_hops (
81+ intermediate_nodes,
82+ recipient_node_id,
83+ 0 ,
84+ local_node_receive_key,
85+ context,
86+ entropy_source,
87+ secp_ctx,
88+ )
89+ }
90+
91+ /// Same as [`BlindedMessagePath::new`], but allows specifying a number of dummy hops.
92+ ///
93+ /// Note:
94+ /// At most [`MAX_DUMMY_HOPS_COUNT`] dummy hops can be added to the blinded path.
95+ pub fn new_with_dummy_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
96+ intermediate_nodes : & [ MessageForwardNode ] , recipient_node_id : PublicKey ,
97+ dummy_hop_count : usize , local_node_receive_key : ReceiveAuthKey , context : MessageContext ,
98+ entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
99+ ) -> Result < Self , ( ) >
77100 where
78101 ES :: Target : EntropySource ,
79102 {
@@ -91,6 +114,7 @@ impl BlindedMessagePath {
91114 secp_ctx,
92115 intermediate_nodes,
93116 recipient_node_id,
117+ dummy_hop_count,
94118 context,
95119 & blinding_secret,
96120 local_node_receive_key,
@@ -266,6 +290,23 @@ pub(crate) struct ForwardTlvs {
266290 pub ( crate ) next_blinding_override : Option < PublicKey > ,
267291}
268292
293+ /// Represents the dummy TLV encoded immediately before the actual [`ReceiveTlvs`] in a blinded path.
294+ /// These TLVs are intended for the final node and are recursively authenticated until the real
295+ /// [`ReceiveTlvs`] is reached.
296+ ///
297+ /// Their purpose is to arbitrarily extend the path length, obscuring the receiver's position in the
298+ /// route and thereby enhancing privacy.
299+ pub ( crate ) struct DummyTlv ;
300+
301+ impl Writeable for DummyTlv {
302+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
303+ encode_tlv_stream ! ( writer, {
304+ ( 65539 , ( ) , required) ,
305+ } ) ;
306+ Ok ( ( ) )
307+ }
308+ }
309+
269310/// Similar to [`ForwardTlvs`], but these TLVs are for the final node.
270311pub ( crate ) struct ReceiveTlvs {
271312 /// If `context` is `Some`, it is used to identify the blinded path that this onion message is
@@ -618,15 +659,24 @@ impl_writeable_tlv_based!(DNSResolverContext, {
618659/// to pad message blinded path's [`BlindedHop`]
619660pub ( crate ) const MESSAGE_PADDING_ROUND_OFF : usize = 100 ;
620661
662+ /// The maximum number of dummy hops that can be added to a blinded path.
663+ /// This is to prevent paths from becoming too long and potentially causing
664+ /// issues with message processing or routing.
665+ pub const MAX_DUMMY_HOPS_COUNT : usize = 10 ;
666+
621667/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
622668pub ( super ) fn blinded_hops < T : secp256k1:: Signing + secp256k1:: Verification > (
623669 secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ MessageForwardNode ] ,
624- recipient_node_id : PublicKey , context : MessageContext , session_priv : & SecretKey ,
625- local_node_receive_key : ReceiveAuthKey ,
670+ recipient_node_id : PublicKey , dummy_hop_count : usize , context : MessageContext ,
671+ session_priv : & SecretKey , local_node_receive_key : ReceiveAuthKey ,
626672) -> Result < Vec < BlindedHop > , secp256k1:: Error > {
673+ let dummy_count = cmp:: min ( dummy_hop_count, MAX_DUMMY_HOPS_COUNT ) ;
627674 let pks = intermediate_nodes
628675 . iter ( )
629676 . map ( |node| ( node. node_id , None ) )
677+ . chain (
678+ core:: iter:: repeat ( ( recipient_node_id, Some ( local_node_receive_key) ) ) . take ( dummy_count) ,
679+ )
630680 . chain ( core:: iter:: once ( ( recipient_node_id, Some ( local_node_receive_key) ) ) ) ;
631681 let is_compact = intermediate_nodes. iter ( ) . any ( |node| node. short_channel_id . is_some ( ) ) ;
632682
@@ -641,6 +691,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
641691 . map ( |next_hop| {
642692 ControlTlvs :: Forward ( ForwardTlvs { next_hop, next_blinding_override : None } )
643693 } )
694+ . chain ( ( 0 ..dummy_count) . map ( |_| ControlTlvs :: Dummy ) )
644695 . chain ( core:: iter:: once ( ControlTlvs :: Receive ( ReceiveTlvs { context : Some ( context) } ) ) ) ;
645696
646697 if is_compact {
0 commit comments