Skip to content

Commit 1d4f860

Browse files
committed
Pass node features through to RouteHops
This exposes the latest Init-context features in the ChannelDetails passed to the Router during route calculation, which combines those with the Node-context features tracked from node_announcements to provide the latest Node-context features in RouteHop structs. Fields are also added for Channel-context features, though those are only partially used since no such features are defined today anyway. These will be useful when determining whether to use new TLV-formatted onion hop datas when generating onions for peers.
1 parent b09b8d8 commit 1d4f860

File tree

7 files changed

+249
-61
lines changed

7 files changed

+249
-61
lines changed

fuzz/src/chanmon_consistency.rs

+6
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,9 @@ pub fn do_test(data: &[u8]) {
422422
if let Err(_) = $source.send_payment(Route {
423423
hops: vec![RouteHop {
424424
pubkey: $dest.0.get_our_node_id(),
425+
node_features: Features::empty(),
425426
short_channel_id: $dest.1,
427+
channel_features: Features::empty(),
426428
fee_msat: 5000000,
427429
cltv_expiry_delta: 200,
428430
}],
@@ -437,12 +439,16 @@ pub fn do_test(data: &[u8]) {
437439
if let Err(_) = $source.send_payment(Route {
438440
hops: vec![RouteHop {
439441
pubkey: $middle.0.get_our_node_id(),
442+
node_features: Features::empty(),
440443
short_channel_id: $middle.1,
444+
channel_features: Features::empty(),
441445
fee_msat: 50000,
442446
cltv_expiry_delta: 100,
443447
},RouteHop {
444448
pubkey: $dest.0.get_our_node_id(),
449+
node_features: Features::empty(),
445450
short_channel_id: $dest.1,
451+
channel_features: Features::empty(),
446452
fee_msat: 5000000,
447453
cltv_expiry_delta: 200,
448454
}],

fuzz/src/router.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use bitcoin::blockdata::transaction::Transaction;
66
use lightning::chain::chaininterface::{ChainError,ChainWatchInterface};
77
use lightning::ln::channelmanager::ChannelDetails;
88
use lightning::ln::msgs;
9-
use lightning::ln::msgs::{RoutingMessageHandler};
9+
use lightning::ln::msgs::{RoutingMessageHandler, Features};
1010
use lightning::ln::router::{Router, RouteHint};
1111
use lightning::util::logger::Logger;
1212
use lightning::util::ser::Readable;
@@ -198,6 +198,7 @@ pub fn do_test(data: &[u8]) {
198198
channel_id: [0; 32],
199199
short_channel_id: Some(slice_to_be64(get_slice!(8))),
200200
remote_network_id: get_pubkey!(),
201+
remote_node_features: Features::empty(),
201202
channel_value_satoshis: slice_to_be64(get_slice!(8)),
202203
user_id: 0,
203204
inbound_capacity_msat: 0,

lightning/src/ln/channelmanager.rs

+57-32
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ pub struct ChannelDetails {
422422
pub short_channel_id: Option<u64>,
423423
/// The node_id of our counterparty
424424
pub remote_network_id: PublicKey,
425+
/// The Features this node provided us opon our last connection with them.
426+
/// This is particularly useful for routing as many node_announcement-context features are also
427+
/// set in the init-context, and we should almost certainly consider the init-context version
428+
/// to be the latest copy.
429+
pub remote_node_features: Features<FeatureContextInit>,
425430
/// The value, in satoshis, of this channel as appears in the funding output
426431
pub channel_value_satoshis: u64,
427432
/// The user_id passed in to create_channel, or 0 if the channel was inbound.
@@ -694,50 +699,70 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
694699
/// Gets the list of open channels, in random order. See ChannelDetail field documentation for
695700
/// more information.
696701
pub fn list_channels(&self) -> Vec<ChannelDetails> {
697-
let channel_state = self.channel_state.lock().unwrap();
698-
let mut res = Vec::with_capacity(channel_state.by_id.len());
699-
for (channel_id, channel) in channel_state.by_id.iter() {
700-
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
701-
res.push(ChannelDetails {
702-
channel_id: (*channel_id).clone(),
703-
short_channel_id: channel.get_short_channel_id(),
704-
remote_network_id: channel.get_their_node_id(),
705-
channel_value_satoshis: channel.get_value_satoshis(),
706-
inbound_capacity_msat,
707-
outbound_capacity_msat,
708-
user_id: channel.get_user_id(),
709-
is_live: channel.is_live(),
710-
});
711-
}
712-
res
713-
}
714-
715-
/// Gets the list of usable channels, in random order. Useful as an argument to
716-
/// Router::get_route to ensure non-announced channels are used.
717-
///
718-
/// These are guaranteed to have their is_live value set to true, see the documentation for
719-
/// ChannelDetails::is_live for more info on exactly what the criteria are.
720-
pub fn list_usable_channels(&self) -> Vec<ChannelDetails> {
721-
let channel_state = self.channel_state.lock().unwrap();
722-
let mut res = Vec::with_capacity(channel_state.by_id.len());
723-
for (channel_id, channel) in channel_state.by_id.iter() {
724-
// Note we use is_live here instead of usable which leads to somewhat confused
725-
// internal/external nomenclature, but that's ok cause that's probably what the user
726-
// really wanted anyway.
727-
if channel.is_live() {
702+
let mut res = Vec::new();
703+
{
704+
let channel_state = self.channel_state.lock().unwrap();
705+
res.reserve(channel_state.by_id.len());
706+
for (channel_id, channel) in channel_state.by_id.iter() {
728707
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
729708
res.push(ChannelDetails {
730709
channel_id: (*channel_id).clone(),
731710
short_channel_id: channel.get_short_channel_id(),
732711
remote_network_id: channel.get_their_node_id(),
712+
remote_node_features: Features::empty(),
733713
channel_value_satoshis: channel.get_value_satoshis(),
734714
inbound_capacity_msat,
735715
outbound_capacity_msat,
736716
user_id: channel.get_user_id(),
737-
is_live: true,
717+
is_live: channel.is_live(),
738718
});
739719
}
740720
}
721+
let per_peer_state = self.per_peer_state.read().unwrap();
722+
for chan in res.iter_mut() {
723+
if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) {
724+
chan.remote_node_features = peer_state.lock().unwrap().latest_features.clone();
725+
}
726+
}
727+
res
728+
}
729+
730+
/// Gets the list of usable channels, in random order. Useful as an argument to
731+
/// Router::get_route to ensure non-announced channels are used.
732+
///
733+
/// These are guaranteed to have their is_live value set to true, see the documentation for
734+
/// ChannelDetails::is_live for more info on exactly what the criteria are.
735+
pub fn list_usable_channels(&self) -> Vec<ChannelDetails> {
736+
let mut res = Vec::new();
737+
{
738+
let channel_state = self.channel_state.lock().unwrap();
739+
res.reserve(channel_state.by_id.len());
740+
for (channel_id, channel) in channel_state.by_id.iter() {
741+
// Note we use is_live here instead of usable which leads to somewhat confused
742+
// internal/external nomenclature, but that's ok cause that's probably what the user
743+
// really wanted anyway.
744+
if channel.is_live() {
745+
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
746+
res.push(ChannelDetails {
747+
channel_id: (*channel_id).clone(),
748+
short_channel_id: channel.get_short_channel_id(),
749+
remote_network_id: channel.get_their_node_id(),
750+
remote_node_features: Features::empty(),
751+
channel_value_satoshis: channel.get_value_satoshis(),
752+
inbound_capacity_msat,
753+
outbound_capacity_msat,
754+
user_id: channel.get_user_id(),
755+
is_live: true,
756+
});
757+
}
758+
}
759+
}
760+
let per_peer_state = self.per_peer_state.read().unwrap();
761+
for chan in res.iter_mut() {
762+
if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) {
763+
chan.remote_node_features = peer_state.lock().unwrap().latest_features.clone();
764+
}
765+
}
741766
res
742767
}
743768

lightning/src/ln/functional_tests.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1022,19 +1022,25 @@ fn fake_network_test() {
10221022
let mut hops = Vec::with_capacity(3);
10231023
hops.push(RouteHop {
10241024
pubkey: nodes[2].node.get_our_node_id(),
1025+
node_features: Features::empty(),
10251026
short_channel_id: chan_2.0.contents.short_channel_id,
1027+
channel_features: Features::empty(),
10261028
fee_msat: 0,
10271029
cltv_expiry_delta: chan_3.0.contents.cltv_expiry_delta as u32
10281030
});
10291031
hops.push(RouteHop {
10301032
pubkey: nodes[3].node.get_our_node_id(),
1033+
node_features: Features::empty(),
10311034
short_channel_id: chan_3.0.contents.short_channel_id,
1035+
channel_features: Features::empty(),
10321036
fee_msat: 0,
10331037
cltv_expiry_delta: chan_4.1.contents.cltv_expiry_delta as u32
10341038
});
10351039
hops.push(RouteHop {
10361040
pubkey: nodes[1].node.get_our_node_id(),
1041+
node_features: Features::empty(),
10371042
short_channel_id: chan_4.0.contents.short_channel_id,
1043+
channel_features: Features::empty(),
10381044
fee_msat: 1000000,
10391045
cltv_expiry_delta: TEST_FINAL_CLTV,
10401046
});
@@ -1045,19 +1051,25 @@ fn fake_network_test() {
10451051
let mut hops = Vec::with_capacity(3);
10461052
hops.push(RouteHop {
10471053
pubkey: nodes[3].node.get_our_node_id(),
1054+
node_features: Features::empty(),
10481055
short_channel_id: chan_4.0.contents.short_channel_id,
1056+
channel_features: Features::empty(),
10491057
fee_msat: 0,
10501058
cltv_expiry_delta: chan_3.1.contents.cltv_expiry_delta as u32
10511059
});
10521060
hops.push(RouteHop {
10531061
pubkey: nodes[2].node.get_our_node_id(),
1062+
node_features: Features::empty(),
10541063
short_channel_id: chan_3.0.contents.short_channel_id,
1064+
channel_features: Features::empty(),
10551065
fee_msat: 0,
10561066
cltv_expiry_delta: chan_2.1.contents.cltv_expiry_delta as u32
10571067
});
10581068
hops.push(RouteHop {
10591069
pubkey: nodes[1].node.get_our_node_id(),
1070+
node_features: Features::empty(),
10601071
short_channel_id: chan_2.0.contents.short_channel_id,
1072+
channel_features: Features::empty(),
10611073
fee_msat: 1000000,
10621074
cltv_expiry_delta: TEST_FINAL_CLTV,
10631075
});

lightning/src/ln/msgs.rs

+26
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,11 @@ pub struct Features<T: FeatureContext> {
110110
// Used to test encoding of diverse msgs
111111
#[cfg(test)]
112112
pub flags: Vec<u8>,
113+
114+
#[cfg(not(test))]
113115
mark: PhantomData<T>,
116+
#[cfg(test)]
117+
pub mark: PhantomData<T>,
114118
}
115119

116120
impl<T: FeatureContext> Clone for Features<T> {
@@ -207,6 +211,28 @@ impl<T: FeatureContext> Features<T> {
207211
}
208212
}
209213

214+
impl Features<FeatureContextNode> {
215+
/// Takes the flags that we know how to interpret in an init-context features that are also
216+
/// relevant in a node-context features and creates a node-context features from them.
217+
pub(crate) fn relevant_init_flags_to_node(init_ctx: &Features<FeatureContextInit>) -> Self {
218+
let mut flags = Vec::new();
219+
if init_ctx.flags.len() > 0 {
220+
// Pull out data_loss_protect and upfront_shutdown_script (bits 0, 1, 4, and 5)
221+
flags.push(init_ctx.flags.last().unwrap() & 0b00110011);
222+
}
223+
Self { flags, mark: PhantomData, }
224+
}
225+
}
226+
227+
impl Features<FeatureContextChannel> {
228+
/// Takes the flags that we know how to interpret in an init-context features that are also
229+
/// relevant in a channel-context features and creates a channel-context features from them.
230+
pub(crate) fn relevant_init_flags_to_channel(_init_ctx: &Features<FeatureContextInit>) -> Self {
231+
// There are currently no channel flags defined that we understand.
232+
Self { flags: Vec::new(), mark: PhantomData, }
233+
}
234+
}
235+
210236
impl<T: FeatureContextInitNode> Features<T> {
211237
pub(crate) fn supports_data_loss_protect(&self) -> bool {
212238
self.flags.len() > 0 && (self.flags[0] & 3) != 0

lightning/src/ln/onion_utils.rs

+5
Original file line numberDiff line numberDiff line change
@@ -444,22 +444,27 @@ mod tests {
444444
hops: vec!(
445445
RouteHop {
446446
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
447+
channel_features: msgs::Features::empty(), node_features: msgs::Features::empty(),
447448
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
448449
},
449450
RouteHop {
450451
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
452+
channel_features: msgs::Features::empty(), node_features: msgs::Features::empty(),
451453
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
452454
},
453455
RouteHop {
454456
pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
457+
channel_features: msgs::Features::empty(), node_features: msgs::Features::empty(),
455458
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
456459
},
457460
RouteHop {
458461
pubkey: PublicKey::from_slice(&hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]).unwrap(),
462+
channel_features: msgs::Features::empty(), node_features: msgs::Features::empty(),
459463
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
460464
},
461465
RouteHop {
462466
pubkey: PublicKey::from_slice(&hex::decode("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()[..]).unwrap(),
467+
channel_features: msgs::Features::empty(), node_features: msgs::Features::empty(),
463468
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
464469
},
465470
),

0 commit comments

Comments
 (0)