Skip to content

Commit c946bbb

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 84d785f commit c946bbb

File tree

7 files changed

+249
-63
lines changed

7 files changed

+249
-63
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 7 additions & 1 deletion
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

Lines changed: 3 additions & 1 deletion
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+
counterparty_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

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ 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 the channel counterparty provided upon last connection.
408+
/// Useful for routing as it is the most up-to-date copy of the counterparty's features and
409+
/// many routing-relevant features are present in the init context.
410+
pub counterparty_features: InitFeatures,
407411
/// The value, in satoshis, of this channel as appears in the funding output
408412
pub channel_value_satoshis: u64,
409413
/// The user_id passed in to create_channel, or 0 if the channel was inbound.
@@ -679,50 +683,70 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
679683
/// Gets the list of open channels, in random order. See ChannelDetail field documentation for
680684
/// more information.
681685
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() {
686+
let mut res = Vec::new();
687+
{
688+
let channel_state = self.channel_state.lock().unwrap();
689+
res.reserve(channel_state.by_id.len());
690+
for (channel_id, channel) in channel_state.by_id.iter() {
713691
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
714692
res.push(ChannelDetails {
715693
channel_id: (*channel_id).clone(),
716694
short_channel_id: channel.get_short_channel_id(),
717695
remote_network_id: channel.get_their_node_id(),
696+
counterparty_features: InitFeatures::empty(),
718697
channel_value_satoshis: channel.get_value_satoshis(),
719698
inbound_capacity_msat,
720699
outbound_capacity_msat,
721700
user_id: channel.get_user_id(),
722-
is_live: true,
701+
is_live: channel.is_live(),
723702
});
724703
}
725704
}
705+
let per_peer_state = self.per_peer_state.read().unwrap();
706+
for chan in res.iter_mut() {
707+
if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) {
708+
chan.counterparty_features = peer_state.lock().unwrap().latest_features.clone();
709+
}
710+
}
711+
res
712+
}
713+
714+
/// Gets the list of usable channels, in random order. Useful as an argument to
715+
/// Router::get_route to ensure non-announced channels are used.
716+
///
717+
/// These are guaranteed to have their is_live value set to true, see the documentation for
718+
/// ChannelDetails::is_live for more info on exactly what the criteria are.
719+
pub fn list_usable_channels(&self) -> Vec<ChannelDetails> {
720+
let mut res = Vec::new();
721+
{
722+
let channel_state = self.channel_state.lock().unwrap();
723+
res.reserve(channel_state.by_id.len());
724+
for (channel_id, channel) in channel_state.by_id.iter() {
725+
// Note we use is_live here instead of usable which leads to somewhat confused
726+
// internal/external nomenclature, but that's ok cause that's probably what the user
727+
// really wanted anyway.
728+
if channel.is_live() {
729+
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
730+
res.push(ChannelDetails {
731+
channel_id: (*channel_id).clone(),
732+
short_channel_id: channel.get_short_channel_id(),
733+
remote_network_id: channel.get_their_node_id(),
734+
counterparty_features: InitFeatures::empty(),
735+
channel_value_satoshis: channel.get_value_satoshis(),
736+
inbound_capacity_msat,
737+
outbound_capacity_msat,
738+
user_id: channel.get_user_id(),
739+
is_live: true,
740+
});
741+
}
742+
}
743+
}
744+
let per_peer_state = self.per_peer_state.read().unwrap();
745+
for chan in res.iter_mut() {
746+
if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) {
747+
chan.counterparty_features = peer_state.lock().unwrap().latest_features.clone();
748+
}
749+
}
726750
res
727751
}
728752

lightning/src/ln/features.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ impl ChannelFeatures {
118118
mark: PhantomData,
119119
}
120120
}
121+
122+
/// Takes the flags that we know how to interpret in an init-context features that are also
123+
/// relevant in a channel-context features and creates a channel-context features from them.
124+
pub(crate) fn with_known_relevant_init_flags(_init_ctx: &InitFeatures) -> Self {
125+
// There are currently no channel flags defined that we understand.
126+
Self { flags: Vec::new(), mark: PhantomData, }
127+
}
121128
}
122129

123130
impl NodeFeatures {
@@ -136,6 +143,17 @@ impl NodeFeatures {
136143
mark: PhantomData,
137144
}
138145
}
146+
147+
/// Takes the flags that we know how to interpret in an init-context features that are also
148+
/// relevant in a node-context features and creates a node-context features from them.
149+
pub(crate) fn with_known_relevant_init_flags(init_ctx: &InitFeatures) -> Self {
150+
let mut flags = Vec::new();
151+
if init_ctx.flags.len() > 0 {
152+
// Pull out data_loss_protect and upfront_shutdown_script (bits 0, 1, 4, and 5)
153+
flags.push(init_ctx.flags.last().unwrap() & 0b00110011);
154+
}
155+
Self { flags, mark: PhantomData, }
156+
}
139157
}
140158

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

lightning/src/ln/functional_tests.rs

Lines changed: 13 additions & 1 deletion
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::{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

Lines changed: 6 additions & 0 deletions
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)