@@ -17,6 +17,7 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
17
17
18
18
use crate :: ln:: msgs:: DecodeError ;
19
19
use crate :: offers:: invoice:: BlindedPayInfo ;
20
+ use crate :: routing:: gossip:: { NodeId , ReadOnlyNetworkGraph } ;
20
21
use crate :: sign:: EntropySource ;
21
22
use crate :: util:: ser:: { Readable , Writeable , Writer } ;
22
23
@@ -28,11 +29,11 @@ use crate::prelude::*;
28
29
#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
29
30
pub struct BlindedPath {
30
31
/// 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
32
33
/// message or payment's next hop and forward it along.
33
34
///
34
35
/// [`encrypted_payload`]: BlindedHop::encrypted_payload
35
- pub introduction_node_id : PublicKey ,
36
+ pub introduction_node : IntroductionNode ,
36
37
/// Used by the introduction node to decrypt its [`encrypted_payload`] to forward the onion
37
38
/// message or payment.
38
39
///
@@ -42,6 +43,52 @@ pub struct BlindedPath {
42
43
pub blinded_hops : Vec < BlindedHop > ,
43
44
}
44
45
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
+
45
92
/// An encrypted payload and node id corresponding to a hop in a payment or onion message path, to
46
93
/// be encoded in the sender's onion packet. These hops cannot be identified by outside observers
47
94
/// and thus can be used to hide the identity of the recipient.
@@ -74,10 +121,10 @@ impl BlindedPath {
74
121
if node_pks. is_empty ( ) { return Err ( ( ) ) }
75
122
let blinding_secret_bytes = entropy_source. get_secure_random_bytes ( ) ;
76
123
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 ] ) ;
78
125
79
126
Ok ( BlindedPath {
80
- introduction_node_id ,
127
+ introduction_node ,
81
128
blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
82
129
blinded_hops : message:: blinded_hops ( secp_ctx, node_pks, & blinding_secret) . map_err ( |_| ( ) ) ?,
83
130
} )
@@ -111,25 +158,59 @@ impl BlindedPath {
111
158
payee_tlvs : payment:: ReceiveTlvs , htlc_maximum_msat : u64 , min_final_cltv_expiry_delta : u16 ,
112
159
entropy_source : & ES , secp_ctx : & Secp256k1 < T >
113
160
) -> Result < ( BlindedPayInfo , Self ) , ( ) > {
161
+ let introduction_node = IntroductionNode :: NodeId (
162
+ intermediate_nodes. first ( ) . map_or ( payee_node_id, |n| n. node_id )
163
+ ) ;
114
164
let blinding_secret_bytes = entropy_source. get_secure_random_bytes ( ) ;
115
165
let blinding_secret = SecretKey :: from_slice ( & blinding_secret_bytes[ ..] ) . expect ( "RNG is busted" ) ;
116
166
117
167
let blinded_payinfo = payment:: compute_payinfo (
118
168
intermediate_nodes, & payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
119
169
) ?;
120
170
Ok ( ( blinded_payinfo, BlindedPath {
121
- introduction_node_id : intermediate_nodes . first ( ) . map_or ( payee_node_id , |n| n . node_id ) ,
171
+ introduction_node ,
122
172
blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
123
173
blinded_hops : payment:: blinded_hops (
124
174
secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, & blinding_secret
125
175
) . map_err ( |_| ( ) ) ?,
126
176
} ) )
127
177
}
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
+ }
128
199
}
129
200
130
201
impl Writeable for BlindedPath {
131
202
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
+
133
214
self . blinding_point . write ( w) ?;
134
215
( self . blinded_hops . len ( ) as u8 ) . write ( w) ?;
135
216
for hop in & self . blinded_hops {
@@ -141,7 +222,17 @@ impl Writeable for BlindedPath {
141
222
142
223
impl Readable for BlindedPath {
143
224
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
+ } ;
145
236
let blinding_point = Readable :: read ( r) ?;
146
237
let num_hops: u8 = Readable :: read ( r) ?;
147
238
if num_hops == 0 { return Err ( DecodeError :: InvalidValue ) }
@@ -150,7 +241,7 @@ impl Readable for BlindedPath {
150
241
blinded_hops. push ( Readable :: read ( r) ?) ;
151
242
}
152
243
Ok ( BlindedPath {
153
- introduction_node_id ,
244
+ introduction_node ,
154
245
blinding_point,
155
246
blinded_hops,
156
247
} )
@@ -162,3 +253,25 @@ impl_writeable!(BlindedHop, {
162
253
encrypted_payload
163
254
} ) ;
164
255
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