@@ -17,6 +17,7 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
1717
1818use crate :: ln:: msgs:: DecodeError ;
1919use crate :: offers:: invoice:: BlindedPayInfo ;
20+ use crate :: routing:: gossip:: { NodeId , ReadOnlyNetworkGraph } ;
2021use crate :: sign:: EntropySource ;
2122use crate :: util:: ser:: { Readable , Writeable , Writer } ;
2223
@@ -28,11 +29,11 @@ use crate::prelude::*;
2829#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
2930pub struct BlindedPath {
3031 /// To send to a blinded path, the sender first finds a route to the unblinded
31- /// `introduction_node_id `, which can unblind its [`encrypted_payload`] to find out the onion
32+ /// `introduction_node `, which can unblind its [`encrypted_payload`] to find out the onion
3233 /// message or payment's next hop and forward it along.
3334 ///
3435 /// [`encrypted_payload`]: BlindedHop::encrypted_payload
35- pub introduction_node_id : PublicKey ,
36+ pub introduction_node : IntroductionNode ,
3637 /// Used by the introduction node to decrypt its [`encrypted_payload`] to forward the onion
3738 /// message or payment.
3839 ///
@@ -42,6 +43,52 @@ pub struct BlindedPath {
4243 pub blinded_hops : Vec < BlindedHop > ,
4344}
4445
46+ /// The unblinded node in a [`BlindedPath`].
47+ #[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
48+ pub enum IntroductionNode {
49+ /// The node id of the introduction node.
50+ NodeId ( PublicKey ) ,
51+ /// The short channel id of the channel leading to the introduction node. The [`Direction`]
52+ /// identifies which side of the channel is the introduction node.
53+ DirectedShortChannelId ( Direction , u64 ) ,
54+ }
55+
56+ /// The side of a channel that is the [`IntroductionNode`] in a [`BlindedPath`]. [BOLT 7] defines
57+ /// which nodes is which in the [`ChannelAnnouncement`] message.
58+ ///
59+ /// [BOLT 7]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_announcement-message
60+ /// [`ChannelAnnouncement`]: crate::ln::msgs::ChannelAnnouncement
61+ #[ derive( Clone , Copy , Debug , Hash , PartialEq , Eq ) ]
62+ pub enum Direction {
63+ /// The lesser node id when compared lexicographically in ascending order.
64+ NodeOne ,
65+ /// The greater node id when compared lexicographically in ascending order.
66+ NodeTwo ,
67+ }
68+
69+ /// An interface for looking up the node id of a channel counterparty for the purpose of forwarding
70+ /// an [`OnionMessage`].
71+ ///
72+ /// [`OnionMessage`]: crate::ln::msgs::OnionMessage
73+ pub trait NodeIdLookUp {
74+ /// Returns the node id of the forwarding node's channel counterparty with `short_channel_id`.
75+ ///
76+ /// Here, the forwarding node is referring to the node of the [`OnionMessenger`] parameterized
77+ /// by the [`NodeIdLookUp`] and the counterparty to one of that node's peers.
78+ ///
79+ /// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
80+ fn next_node_id ( & self , short_channel_id : u64 ) -> Option < PublicKey > ;
81+ }
82+
83+ /// A [`NodeIdLookUp`] that always returns `None`.
84+ pub struct EmptyNodeIdLookUp { }
85+
86+ impl NodeIdLookUp for EmptyNodeIdLookUp {
87+ fn next_node_id ( & self , _short_channel_id : u64 ) -> Option < PublicKey > {
88+ None
89+ }
90+ }
91+
4592/// An encrypted payload and node id corresponding to a hop in a payment or onion message path, to
4693/// be encoded in the sender's onion packet. These hops cannot be identified by outside observers
4794/// and thus can be used to hide the identity of the recipient.
@@ -74,10 +121,10 @@ impl BlindedPath {
74121 if node_pks. is_empty ( ) { return Err ( ( ) ) }
75122 let blinding_secret_bytes = entropy_source. get_secure_random_bytes ( ) ;
76123 let blinding_secret = SecretKey :: from_slice ( & blinding_secret_bytes[ ..] ) . expect ( "RNG is busted" ) ;
77- let introduction_node_id = node_pks[ 0 ] ;
124+ let introduction_node = IntroductionNode :: NodeId ( node_pks[ 0 ] ) ;
78125
79126 Ok ( BlindedPath {
80- introduction_node_id ,
127+ introduction_node ,
81128 blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
82129 blinded_hops : message:: blinded_hops ( secp_ctx, node_pks, & blinding_secret) . map_err ( |_| ( ) ) ?,
83130 } )
@@ -111,25 +158,59 @@ impl BlindedPath {
111158 payee_tlvs : payment:: ReceiveTlvs , htlc_maximum_msat : u64 , min_final_cltv_expiry_delta : u16 ,
112159 entropy_source : & ES , secp_ctx : & Secp256k1 < T >
113160 ) -> Result < ( BlindedPayInfo , Self ) , ( ) > {
161+ let introduction_node = IntroductionNode :: NodeId (
162+ intermediate_nodes. first ( ) . map_or ( payee_node_id, |n| n. node_id )
163+ ) ;
114164 let blinding_secret_bytes = entropy_source. get_secure_random_bytes ( ) ;
115165 let blinding_secret = SecretKey :: from_slice ( & blinding_secret_bytes[ ..] ) . expect ( "RNG is busted" ) ;
116166
117167 let blinded_payinfo = payment:: compute_payinfo (
118168 intermediate_nodes, & payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
119169 ) ?;
120170 Ok ( ( blinded_payinfo, BlindedPath {
121- introduction_node_id : intermediate_nodes . first ( ) . map_or ( payee_node_id , |n| n . node_id ) ,
171+ introduction_node ,
122172 blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
123173 blinded_hops : payment:: blinded_hops (
124174 secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, & blinding_secret
125175 ) . map_err ( |_| ( ) ) ?,
126176 } ) )
127177 }
178+
179+ /// Returns the introduction [`NodeId`] of the blinded path, if it is publicly reachable (i.e.,
180+ /// it is found in the network graph).
181+ pub fn public_introduction_node_id < ' a > (
182+ & self , network_graph : & ' a ReadOnlyNetworkGraph
183+ ) -> Option < & ' a NodeId > {
184+ match & self . introduction_node {
185+ IntroductionNode :: NodeId ( pubkey) => {
186+ let node_id = NodeId :: from_pubkey ( pubkey) ;
187+ network_graph. nodes ( ) . get_key_value ( & node_id) . map ( |( key, _) | key)
188+ } ,
189+ IntroductionNode :: DirectedShortChannelId ( direction, scid) => {
190+ network_graph
191+ . channel ( * scid)
192+ . map ( |c| match direction {
193+ Direction :: NodeOne => & c. node_one ,
194+ Direction :: NodeTwo => & c. node_two ,
195+ } )
196+ } ,
197+ }
198+ }
128199}
129200
130201impl Writeable for BlindedPath {
131202 fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
132- self . introduction_node_id . write ( w) ?;
203+ match & self . introduction_node {
204+ IntroductionNode :: NodeId ( pubkey) => pubkey. write ( w) ?,
205+ IntroductionNode :: DirectedShortChannelId ( direction, scid) => {
206+ match direction {
207+ Direction :: NodeOne => 0u8 . write ( w) ?,
208+ Direction :: NodeTwo => 1u8 . write ( w) ?,
209+ }
210+ scid. write ( w) ?;
211+ } ,
212+ }
213+
133214 self . blinding_point . write ( w) ?;
134215 ( self . blinded_hops . len ( ) as u8 ) . write ( w) ?;
135216 for hop in & self . blinded_hops {
@@ -141,7 +222,17 @@ impl Writeable for BlindedPath {
141222
142223impl Readable for BlindedPath {
143224 fn read < R : io:: Read > ( r : & mut R ) -> Result < Self , DecodeError > {
144- let introduction_node_id = Readable :: read ( r) ?;
225+ let mut first_byte: u8 = Readable :: read ( r) ?;
226+ let introduction_node = match first_byte {
227+ 0 => IntroductionNode :: DirectedShortChannelId ( Direction :: NodeOne , Readable :: read ( r) ?) ,
228+ 1 => IntroductionNode :: DirectedShortChannelId ( Direction :: NodeTwo , Readable :: read ( r) ?) ,
229+ 2 |3 => {
230+ use io:: Read ;
231+ let mut pubkey_read = core:: slice:: from_mut ( & mut first_byte) . chain ( r. by_ref ( ) ) ;
232+ IntroductionNode :: NodeId ( Readable :: read ( & mut pubkey_read) ?)
233+ } ,
234+ _ => return Err ( DecodeError :: InvalidValue ) ,
235+ } ;
145236 let blinding_point = Readable :: read ( r) ?;
146237 let num_hops: u8 = Readable :: read ( r) ?;
147238 if num_hops == 0 { return Err ( DecodeError :: InvalidValue ) }
@@ -150,7 +241,7 @@ impl Readable for BlindedPath {
150241 blinded_hops. push ( Readable :: read ( r) ?) ;
151242 }
152243 Ok ( BlindedPath {
153- introduction_node_id ,
244+ introduction_node ,
154245 blinding_point,
155246 blinded_hops,
156247 } )
@@ -162,3 +253,25 @@ impl_writeable!(BlindedHop, {
162253 encrypted_payload
163254} ) ;
164255
256+ impl Direction {
257+ /// Returns the [`NodeId`] from the inputs corresponding to the direction.
258+ pub fn select_node_id < ' a > ( & self , node_a : & ' a NodeId , node_b : & ' a NodeId ) -> & ' a NodeId {
259+ match self {
260+ Direction :: NodeOne => core:: cmp:: min ( node_a, node_b) ,
261+ Direction :: NodeTwo => core:: cmp:: max ( node_a, node_b) ,
262+ }
263+ }
264+
265+ /// Returns the [`PublicKey`] from the inputs corresponding to the direction.
266+ pub fn select_pubkey < ' a > ( & self , node_a : & ' a PublicKey , node_b : & ' a PublicKey ) -> & ' a PublicKey {
267+ let ( node_one, node_two) = if NodeId :: from_pubkey ( node_a) < NodeId :: from_pubkey ( node_b) {
268+ ( node_a, node_b)
269+ } else {
270+ ( node_b, node_a)
271+ } ;
272+ match self {
273+ Direction :: NodeOne => node_one,
274+ Direction :: NodeTwo => node_two,
275+ }
276+ }
277+ }
0 commit comments