Skip to content

Commit 28abb40

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 a29d25a commit 28abb40

File tree

7 files changed

+250
-63
lines changed

7 files changed

+250
-63
lines changed

fuzz/src/chanmon_consistency.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use lightning::ln::channelmonitor;
2929
use lightning::ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdateErr, HTLCUpdate};
3030
use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, ChannelManagerReadArgs};
3131
use lightning::ln::router::{Route, RouteHop};
32-
use lightning::ln::features::InitFeatures;
32+
use lightning::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
3333
use lightning::ln::msgs::{CommitmentUpdate, ChannelMessageHandler, ErrorAction, UpdateAddHTLC, Init};
3434
use lightning::util::enforcing_trait_impls::EnforcingChannelKeys;
3535
use lightning::util::events;
@@ -414,7 +414,9 @@ pub fn do_test(data: &[u8]) {
414414
if let Err(_) = $source.send_payment(Route {
415415
hops: vec![RouteHop {
416416
pubkey: $dest.0.get_our_node_id(),
417+
node_features: NodeFeatures::empty(),
417418
short_channel_id: $dest.1,
419+
channel_features: ChannelFeatures::empty(),
418420
fee_msat: 5000000,
419421
cltv_expiry_delta: 200,
420422
}],
@@ -429,12 +431,16 @@ pub fn do_test(data: &[u8]) {
429431
if let Err(_) = $source.send_payment(Route {
430432
hops: vec![RouteHop {
431433
pubkey: $middle.0.get_our_node_id(),
434+
node_features: NodeFeatures::empty(),
432435
short_channel_id: $middle.1,
436+
channel_features: ChannelFeatures::empty(),
433437
fee_msat: 50000,
434438
cltv_expiry_delta: 100,
435439
},RouteHop {
436440
pubkey: $dest.0.get_our_node_id(),
441+
node_features: NodeFeatures::empty(),
437442
short_channel_id: $dest.1,
443+
channel_features: ChannelFeatures::empty(),
438444
fee_msat: 5000000,
439445
cltv_expiry_delta: 200,
440446
}],

fuzz/src/router.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use bitcoin::blockdata::transaction::Transaction;
55

66
use lightning::chain::chaininterface::{ChainError,ChainWatchInterface};
77
use lightning::ln::channelmanager::ChannelDetails;
8+
use lightning::ln::features::InitFeatures;
89
use lightning::ln::msgs;
9-
use lightning::ln::msgs::{RoutingMessageHandler};
10+
use lightning::ln::msgs::RoutingMessageHandler;
1011
use lightning::ln::router::{Router, RouteHint};
1112
use lightning::util::logger::Logger;
1213
use lightning::util::ser::Readable;
@@ -198,6 +199,7 @@ pub fn do_test(data: &[u8]) {
198199
channel_id: [0; 32],
199200
short_channel_id: Some(slice_to_be64(get_slice!(8))),
200201
remote_network_id: get_pubkey!(),
202+
remote_node_features: InitFeatures::empty(),
201203
channel_value_satoshis: slice_to_be64(get_slice!(8)),
202204
user_id: 0,
203205
inbound_capacity_msat: 0,

lightning/src/ln/channelmanager.rs

+57-32
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,11 @@ pub struct ChannelDetails {
404404
pub short_channel_id: Option<u64>,
405405
/// The node_id of our counterparty
406406
pub remote_network_id: PublicKey,
407+
/// The Features this node provided us opon our last connection with them.
408+
/// This is particularly useful for routing as many node_announcement-context features are also
409+
/// set in the init-context, and we should almost certainly consider the init-context version
410+
/// to be the latest copy.
411+
pub remote_node_features: InitFeatures,
407412
/// The value, in satoshis, of this channel as appears in the funding output
408413
pub channel_value_satoshis: u64,
409414
/// The user_id passed in to create_channel, or 0 if the channel was inbound.
@@ -679,50 +684,70 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
679684
/// Gets the list of open channels, in random order. See ChannelDetail field documentation for
680685
/// more information.
681686
pub fn list_channels(&self) -> Vec<ChannelDetails> {
682-
let channel_state = self.channel_state.lock().unwrap();
683-
let mut res = Vec::with_capacity(channel_state.by_id.len());
684-
for (channel_id, channel) in channel_state.by_id.iter() {
685-
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
686-
res.push(ChannelDetails {
687-
channel_id: (*channel_id).clone(),
688-
short_channel_id: channel.get_short_channel_id(),
689-
remote_network_id: channel.get_their_node_id(),
690-
channel_value_satoshis: channel.get_value_satoshis(),
691-
inbound_capacity_msat,
692-
outbound_capacity_msat,
693-
user_id: channel.get_user_id(),
694-
is_live: channel.is_live(),
695-
});
696-
}
697-
res
698-
}
699-
700-
/// Gets the list of usable channels, in random order. Useful as an argument to
701-
/// Router::get_route to ensure non-announced channels are used.
702-
///
703-
/// These are guaranteed to have their is_live value set to true, see the documentation for
704-
/// ChannelDetails::is_live for more info on exactly what the criteria are.
705-
pub fn list_usable_channels(&self) -> Vec<ChannelDetails> {
706-
let channel_state = self.channel_state.lock().unwrap();
707-
let mut res = Vec::with_capacity(channel_state.by_id.len());
708-
for (channel_id, channel) in channel_state.by_id.iter() {
709-
// Note we use is_live here instead of usable which leads to somewhat confused
710-
// internal/external nomenclature, but that's ok cause that's probably what the user
711-
// really wanted anyway.
712-
if channel.is_live() {
687+
let mut res = Vec::new();
688+
{
689+
let channel_state = self.channel_state.lock().unwrap();
690+
res.reserve(channel_state.by_id.len());
691+
for (channel_id, channel) in channel_state.by_id.iter() {
713692
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
714693
res.push(ChannelDetails {
715694
channel_id: (*channel_id).clone(),
716695
short_channel_id: channel.get_short_channel_id(),
717696
remote_network_id: channel.get_their_node_id(),
697+
remote_node_features: InitFeatures::empty(),
718698
channel_value_satoshis: channel.get_value_satoshis(),
719699
inbound_capacity_msat,
720700
outbound_capacity_msat,
721701
user_id: channel.get_user_id(),
722-
is_live: true,
702+
is_live: channel.is_live(),
723703
});
724704
}
725705
}
706+
let per_peer_state = self.per_peer_state.read().unwrap();
707+
for chan in res.iter_mut() {
708+
if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) {
709+
chan.remote_node_features = peer_state.lock().unwrap().latest_features.clone();
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 mut res = Vec::new();
722+
{
723+
let channel_state = self.channel_state.lock().unwrap();
724+
res.reserve(channel_state.by_id.len());
725+
for (channel_id, channel) in channel_state.by_id.iter() {
726+
// Note we use is_live here instead of usable which leads to somewhat confused
727+
// internal/external nomenclature, but that's ok cause that's probably what the user
728+
// really wanted anyway.
729+
if channel.is_live() {
730+
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
731+
res.push(ChannelDetails {
732+
channel_id: (*channel_id).clone(),
733+
short_channel_id: channel.get_short_channel_id(),
734+
remote_network_id: channel.get_their_node_id(),
735+
remote_node_features: InitFeatures::empty(),
736+
channel_value_satoshis: channel.get_value_satoshis(),
737+
inbound_capacity_msat,
738+
outbound_capacity_msat,
739+
user_id: channel.get_user_id(),
740+
is_live: true,
741+
});
742+
}
743+
}
744+
}
745+
let per_peer_state = self.per_peer_state.read().unwrap();
746+
for chan in res.iter_mut() {
747+
if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) {
748+
chan.remote_node_features = peer_state.lock().unwrap().latest_features.clone();
749+
}
750+
}
726751
res
727752
}
728753

lightning/src/ln/features.rs

+18
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ impl ChannelFeatures {
126126
mark: PhantomData,
127127
}
128128
}
129+
130+
/// Takes the flags that we know how to interpret in an init-context features that are also
131+
/// relevant in a channel-context features and creates a channel-context features from them.
132+
pub(crate) fn relevant_init_flags_to_channel(_init_ctx: &InitFeatures) -> Self {
133+
// There are currently no channel flags defined that we understand.
134+
Self { flags: Vec::new(), mark: PhantomData, }
135+
}
129136
}
130137

131138
impl NodeFeatures {
@@ -144,6 +151,17 @@ impl NodeFeatures {
144151
mark: PhantomData,
145152
}
146153
}
154+
155+
/// Takes the flags that we know how to interpret in an init-context features that are also
156+
/// relevant in a node-context features and creates a node-context features from them.
157+
pub(crate) fn relevant_init_flags_to_node(init_ctx: &InitFeatures) -> Self {
158+
let mut flags = Vec::new();
159+
if init_ctx.flags.len() > 0 {
160+
// Pull out data_loss_protect and upfront_shutdown_script (bits 0, 1, 4, and 5)
161+
flags.push(init_ctx.flags.last().unwrap() & 0b00110011);
162+
}
163+
Self { flags, mark: PhantomData, }
164+
}
147165
}
148166

149167
impl<T: sealed::Context> Features<T> {

lightning/src/ln/functional_tests.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use ln::channelmonitor::{ChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD
1111
use ln::channel::{ACCEPTED_HTLC_SCRIPT_WEIGHT, OFFERED_HTLC_SCRIPT_WEIGHT, Channel, ChannelError};
1212
use ln::onion_utils;
1313
use ln::router::{Route, RouteHop};
14-
use ln::features::{ChannelFeatures, InitFeatures};
14+
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
1515
use ln::msgs;
1616
use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction};
1717
use util::enforcing_trait_impls::EnforcingChannelKeys;
@@ -1029,19 +1029,25 @@ fn fake_network_test() {
10291029
let mut hops = Vec::with_capacity(3);
10301030
hops.push(RouteHop {
10311031
pubkey: nodes[2].node.get_our_node_id(),
1032+
node_features: NodeFeatures::empty(),
10321033
short_channel_id: chan_2.0.contents.short_channel_id,
1034+
channel_features: ChannelFeatures::empty(),
10331035
fee_msat: 0,
10341036
cltv_expiry_delta: chan_3.0.contents.cltv_expiry_delta as u32
10351037
});
10361038
hops.push(RouteHop {
10371039
pubkey: nodes[3].node.get_our_node_id(),
1040+
node_features: NodeFeatures::empty(),
10381041
short_channel_id: chan_3.0.contents.short_channel_id,
1042+
channel_features: ChannelFeatures::empty(),
10391043
fee_msat: 0,
10401044
cltv_expiry_delta: chan_4.1.contents.cltv_expiry_delta as u32
10411045
});
10421046
hops.push(RouteHop {
10431047
pubkey: nodes[1].node.get_our_node_id(),
1048+
node_features: NodeFeatures::empty(),
10441049
short_channel_id: chan_4.0.contents.short_channel_id,
1050+
channel_features: ChannelFeatures::empty(),
10451051
fee_msat: 1000000,
10461052
cltv_expiry_delta: TEST_FINAL_CLTV,
10471053
});
@@ -1052,19 +1058,25 @@ fn fake_network_test() {
10521058
let mut hops = Vec::with_capacity(3);
10531059
hops.push(RouteHop {
10541060
pubkey: nodes[3].node.get_our_node_id(),
1061+
node_features: NodeFeatures::empty(),
10551062
short_channel_id: chan_4.0.contents.short_channel_id,
1063+
channel_features: ChannelFeatures::empty(),
10561064
fee_msat: 0,
10571065
cltv_expiry_delta: chan_3.1.contents.cltv_expiry_delta as u32
10581066
});
10591067
hops.push(RouteHop {
10601068
pubkey: nodes[2].node.get_our_node_id(),
1069+
node_features: NodeFeatures::empty(),
10611070
short_channel_id: chan_3.0.contents.short_channel_id,
1071+
channel_features: ChannelFeatures::empty(),
10621072
fee_msat: 0,
10631073
cltv_expiry_delta: chan_2.1.contents.cltv_expiry_delta as u32
10641074
});
10651075
hops.push(RouteHop {
10661076
pubkey: nodes[1].node.get_our_node_id(),
1077+
node_features: NodeFeatures::empty(),
10671078
short_channel_id: chan_2.0.contents.short_channel_id,
1079+
channel_features: ChannelFeatures::empty(),
10681080
fee_msat: 1000000,
10691081
cltv_expiry_delta: TEST_FINAL_CLTV,
10701082
});

lightning/src/ln/onion_utils.rs

+6
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<
425425
#[cfg(test)]
426426
mod tests {
427427
use ln::channelmanager::PaymentHash;
428+
use ln::features::{ChannelFeatures, NodeFeatures};
428429
use ln::router::{Route, RouteHop};
429430
use ln::msgs;
430431
use util::ser::Writeable;
@@ -444,22 +445,27 @@ mod tests {
444445
hops: vec!(
445446
RouteHop {
446447
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
448+
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
447449
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
448450
},
449451
RouteHop {
450452
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
453+
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
451454
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
452455
},
453456
RouteHop {
454457
pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
458+
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
455459
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
456460
},
457461
RouteHop {
458462
pubkey: PublicKey::from_slice(&hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]).unwrap(),
463+
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
459464
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
460465
},
461466
RouteHop {
462467
pubkey: PublicKey::from_slice(&hex::decode("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()[..]).unwrap(),
468+
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
463469
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
464470
},
465471
),

0 commit comments

Comments
 (0)