Skip to content

Default to BOLT 4 tlv payload format onions #1414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions lightning/src/ln/functional_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1171,19 +1171,18 @@ macro_rules! get_payment_preimage_hash {
#[macro_export]
macro_rules! get_route_and_payment_hash {
($send_node: expr, $recv_node: expr, $recv_value: expr) => {{
$crate::get_route_and_payment_hash!($send_node, $recv_node, vec![], $recv_value, TEST_FINAL_CLTV)
let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id())
.with_features($crate::ln::features::InvoiceFeatures::known());
$crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value, TEST_FINAL_CLTV)
}};
($send_node: expr, $recv_node: expr, $last_hops: expr, $recv_value: expr, $cltv: expr) => {{
($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr, $cltv: expr) => {{
use $crate::chain::keysinterface::KeysInterface;
let (payment_preimage, payment_hash, payment_secret) = $crate::get_payment_preimage_hash!($recv_node, Some($recv_value));
let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id())
.with_features($crate::ln::features::InvoiceFeatures::known())
.with_route_hints($last_hops);
let scorer = $crate::util::test_utils::TestScorer::with_penalty(0);
let keys_manager = $crate::util::test_utils::TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let route = $crate::routing::router::get_route(
&$send_node.node.get_our_node_id(), &payment_params, &$send_node.network_graph.read_only(),
&$send_node.node.get_our_node_id(), &$payment_params, &$send_node.network_graph.read_only(),
Some(&$send_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
$recv_value, $cltv, $send_node.logger, &scorer, &random_seed_bytes
).unwrap();
Expand Down
12 changes: 9 additions & 3 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5188,7 +5188,9 @@ fn test_duplicate_payment_hash_one_failure_one_success() {
// We reduce the final CLTV here by a somewhat arbitrary constant to keep it under the one-byte
// script push size limit so that the below script length checks match
// ACCEPTED_HTLC_SCRIPT_WEIGHT.
let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[3], vec![], 900000, TEST_FINAL_CLTV - 40);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id())
.with_features(InvoiceFeatures::known());
let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[3], payment_params, 900000, TEST_FINAL_CLTV - 40);
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[2], &nodes[3]]], 900000, duplicate_payment_hash, payment_secret);

let commitment_txn = get_local_commitment_txn!(nodes[2], chan_2.2);
Expand Down Expand Up @@ -6437,7 +6439,9 @@ fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() {
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 0, InitFeatures::known(), InitFeatures::known());

let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], vec![], 100000000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id())
.with_features(InvoiceFeatures::known());
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000, 0);
route.paths[0].last_mut().unwrap().cltv_expiry_delta = 500000001;
unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::RouteError { ref err },
assert_eq!(err, &"Channel CLTV overflowed?"));
Expand Down Expand Up @@ -7537,7 +7541,9 @@ fn test_bump_penalty_txn_on_revoked_commitment() {
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000, InitFeatures::known(), InitFeatures::known());

let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
let (route,_, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], vec![], 3000000, 30);
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id())
.with_features(InvoiceFeatures::known());
let (route,_, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_params, 3000000, 30);
send_along_route(&nodes[1], route, &vec!(&nodes[0])[..], 3000000);

let revoked_txn = get_local_commitment_txn!(nodes[0], chan.2);
Expand Down
166 changes: 164 additions & 2 deletions lightning/src/ln/onion_route_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use chain::keysinterface::{KeysInterface, Recipient};
use ln::{PaymentHash, PaymentSecret};
use ln::channelmanager::{HTLCForwardInfo, CLTV_FAR_FAR_AWAY, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting};
use ln::onion_utils;
use routing::network_graph::{NetworkUpdate, RoutingFees};
use routing::network_graph::{NetworkUpdate, RoutingFees, NodeId};
use routing::router::{get_route, PaymentParameters, Route, RouteHint, RouteHintHop};
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::features::{InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler, ChannelUpdate, OptionalField};
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
Expand Down Expand Up @@ -577,6 +577,168 @@ fn test_onion_failure() {
}, true, Some(23), None, None);
}

#[test]
fn test_default_to_onion_payload_tlv_format() {
// Tests that we default to creating tlv format onion payloads when no `NodeAnnouncementInfo`
// `features` for a node in the `network_graph` exists, or when the node isn't in the
// `network_graph`, and no other known `features` for the node exists.
let mut priv_channels_conf = UserConfig::default();
priv_channels_conf.channel_options.announced_channel = false;
let chanmon_cfgs = create_chanmon_cfgs(5);
let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, Some(priv_channels_conf)]);
let mut nodes = create_network(5, &node_cfgs, &node_chanmgrs);

create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::known(), InitFeatures::known());
create_unannounced_chan_between_nodes_with_value(&nodes, 3, 4, 100000, 10001, InitFeatures::known(), InitFeatures::known());

let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id());
let origin_node = &nodes[0];
let network_graph = origin_node.network_graph;

// Clears all the `NodeAnnouncementInfo` for all nodes of `nodes[0]`'s `network_graph`, so that
// their `features` aren't used when creating the `route`.
network_graph.clear_nodes_announcement_info();

let (announced_route, _, _, _) = get_route_and_payment_hash!(
origin_node, nodes[3], payment_params, 10_000, TEST_FINAL_CLTV);

let hops = &announced_route.paths[0];
// Assert that the hop between `nodes[1]` and `nodes[2]` defaults to supporting variable length
// onions, as `nodes[0]` has no `NodeAnnouncementInfo` `features` for `node[2]`
assert!(hops[1].node_features.supports_variable_length_onion());
// Assert that the hop between `nodes[2]` and `nodes[3]` defaults to supporting variable length
// onions, as `nodes[0]` has no `NodeAnnouncementInfo` `features` for `node[3]`, and no `InvoiceFeatures`
// for the `payment_params`, which would otherwise have been used.
assert!(hops[2].node_features.supports_variable_length_onion());
// Note that we do not assert that `hops[0]` (the channel between `nodes[0]` and `nodes[1]`)
// supports variable length onions, as the `InitFeatures` exchanged in the init message
// between the nodes will be used when creating the route. We therefore do not default to
// supporting variable length onions for that hop, as the `InitFeatures` in this case are
// `InitFeatures::known()`.

let unannounced_chan = &nodes[4].node.list_usable_channels()[0];

let last_hop = RouteHint(vec![RouteHintHop {
src_node_id: nodes[3].node.get_our_node_id(),
short_channel_id: unannounced_chan.short_channel_id.unwrap(),
fees: RoutingFees {
base_msat: 0,
proportional_millionths: 0,
},
cltv_expiry_delta: 42,
htlc_minimum_msat: None,
htlc_maximum_msat: None,
}]);

let unannounced_chan_params = PaymentParameters::from_node_id(nodes[4].node.get_our_node_id()).with_route_hints(vec![last_hop]);
let (unannounced_route, _, _, _) = get_route_and_payment_hash!(
origin_node, nodes[4], unannounced_chan_params, 10_000, TEST_FINAL_CLTV);

let unannounced_chan_hop = &unannounced_route.paths[0][3];
// Ensure that `nodes[4]` doesn't exist in `nodes[0]`'s `network_graph`, as it's not public.
assert!(&network_graph.read_only().nodes().get(&NodeId::from_pubkey(&nodes[4].node.get_our_node_id())).is_none());
// Assert that the hop between `nodes[3]` and `nodes[4]` defaults to supporting variable length
// onions, even though `nodes[4]` as `nodes[0]` doesn't exists in `nodes[0]`'s `network_graph`,
// and no `InvoiceFeatures` for the `payment_params` exists, which would otherwise have been
// used.
assert!(unannounced_chan_hop.node_features.supports_variable_length_onion());

let cur_height = nodes[0].best_block_info().1 + 1;
let (announced_route_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&announced_route.paths[0], 40000, &None, cur_height, &None).unwrap();
let (unannounced_route_paylods, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&unannounced_route.paths[0], 40000, &None, cur_height, &None).unwrap();

for onion_payloads in vec![announced_route_payloads, unannounced_route_paylods] {
for onion_payload in onion_payloads.iter() {
match onion_payload.format {
msgs::OnionHopDataFormat::Legacy {..} => {
panic!("Generated a `msgs::OnionHopDataFormat::Legacy` payload, even though that shouldn't have happend.");
}
_ => {}
}
}
}
}

#[test]
fn test_do_not_default_to_onion_payload_tlv_format_when_unsupported() {
// Tests that we do not default to creating tlv onions if either of these types features
// exists, which specifies no support for variable length onions for a specific hop, when
// creating a route:
// 1. `InitFeatures` to the counterparty node exchanged with the init message to the node.
// 2. `NodeFeatures` in the `NodeAnnouncementInfo` of a node in sender node's `network_graph`.
// 3. `InvoiceFeatures` specified by the receiving node, when no `NodeAnnouncementInfo`
// `features` exists for the receiver in the sender's `network_graph`.
let chanmon_cfgs = create_chanmon_cfgs(4);
let mut node_cfgs = create_node_cfgs(4, &chanmon_cfgs);

// Set `node[1]` config to `InitFeatures::empty()` which return `false` for
// `supports_variable_length_onion()`
let mut node_1_cfg = &mut node_cfgs[1];
node_1_cfg.features = InitFeatures::empty();

let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
let mut nodes = create_network(4, &node_cfgs, &node_chanmgrs);

create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::known(), InitFeatures::known());

let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id())
.with_features(InvoiceFeatures::empty());
let origin_node = &nodes[0];
let network_graph = origin_node.network_graph;
network_graph.clear_nodes_announcement_info();

// Set `NodeAnnouncementInfo` `features` which do not support variable length onions for
// `nodes[2]` in `nodes[0]`'s `network_graph`.
let nodes_2_unsigned_node_announcement = msgs::UnsignedNodeAnnouncement {
features: NodeFeatures::empty(),
timestamp: 0,
node_id: nodes[2].node.get_our_node_id(),
rgb: [32; 3],
alias: [16;32],
addresses: Vec::new(),
excess_address_data: Vec::new(),
excess_data: Vec::new(),
};
let _res = network_graph.update_node_from_unsigned_announcement(&nodes_2_unsigned_node_announcement);

let (route, _, _, _) = get_route_and_payment_hash!(
origin_node, nodes[3], payment_params, 10_000, TEST_FINAL_CLTV);

let hops = &route.paths[0];

// Assert that the hop between `nodes[0]` and `nodes[1]` doesn't support variable length
// onions, as as the `InitFeatures` exchanged (`InitFeatures::empty()`) in the init message
// between the nodes when setting up the channel is used when creating the `route` and that we
// therefore do not default to supporting variable length onions. Despite `nodes[0]` having no
// `NodeAnnouncementInfo` `features` for `node[1]`.
assert!(!hops[0].node_features.supports_variable_length_onion());
// Assert that the hop between `nodes[1]` and `nodes[2]` uses the `features` from
// `nodes_2_unsigned_node_announcement` that doesn't support variable length onions.
assert!(!hops[1].node_features.supports_variable_length_onion());
// Assert that the hop between `nodes[2]` and `nodes[3]` uses the `InvoiceFeatures` set to the
// `payment_params`, that doesn't support variable length onions. We therefore do not end up
// defaulting to supporting variable length onions, despite `nodes[0]` having no
// `NodeAnnouncementInfo` `features` for `node[3]`.
assert!(!hops[2].node_features.supports_variable_length_onion());

let cur_height = nodes[0].best_block_info().1 + 1;
let (onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 40000, &None, cur_height, &None).unwrap();

for onion_payload in onion_payloads.iter() {
match onion_payload.format {
msgs::OnionHopDataFormat::Legacy {..} => {}
_ => {
panic!("Should have only have generated `msgs::OnionHopDataFormat::Legacy` payloads");
}
}
}
}

macro_rules! get_phantom_route {
($nodes: expr, $amt: expr, $channel: expr) => {{
let secp_ctx = Secp256k1::new();
Expand Down
29 changes: 22 additions & 7 deletions lightning/src/ln/priv_short_conf_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use chain::channelmonitor::ChannelMonitor;
use chain::keysinterface::{Recipient, KeysInterface};
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, MIN_CLTV_EXPIRY_DELTA};
use routing::network_graph::RoutingFees;
use routing::router::{RouteHint, RouteHintHop};
use ln::features::InitFeatures;
use routing::router::{PaymentParameters, RouteHint, RouteHintHop};
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, OptionalField};
use util::enforcing_trait_impls::EnforcingSigner;
Expand Down Expand Up @@ -71,7 +71,10 @@ fn test_priv_forwarding_rejection() {
htlc_maximum_msat: None,
}]);
let last_hops = vec![route_hint];
let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], last_hops, 10_000, TEST_FINAL_CLTV);
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
.with_features(InvoiceFeatures::known())
.with_route_hints(last_hops);
let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000, TEST_FINAL_CLTV);

nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
Expand Down Expand Up @@ -269,7 +272,10 @@ fn test_routed_scid_alias() {
htlc_maximum_msat: None,
htlc_minimum_msat: None,
}])];
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], hop_hints, 100_000, 42);
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
.with_features(InvoiceFeatures::known())
.with_route_hints(hop_hints);
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000, 42);
assert_eq!(route.paths[0][1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
Expand Down Expand Up @@ -419,7 +425,10 @@ fn test_inbound_scid_privacy() {
htlc_maximum_msat: None,
htlc_minimum_msat: None,
}])];
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], hop_hints.clone(), 100_000, 42);
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
.with_features(InvoiceFeatures::known())
.with_route_hints(hop_hints.clone());
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000, 42);
assert_eq!(route.paths[0][1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
Expand All @@ -431,7 +440,10 @@ fn test_inbound_scid_privacy() {
// what channel we're talking about.
hop_hints[0].0[0].short_channel_id = last_hop[0].short_channel_id.unwrap();

let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], hop_hints, 100_000, 42);
let payment_params_2 = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
.with_features(InvoiceFeatures::known())
.with_route_hints(hop_hints);
let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000, 42);
assert_eq!(route_2.paths[0][1].short_channel_id, last_hop[0].short_channel_id.unwrap());
nodes[0].node.send_payment(&route_2, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[0], 1);
Expand Down Expand Up @@ -479,7 +491,10 @@ fn test_scid_alias_returned() {
htlc_maximum_msat: None,
htlc_minimum_msat: None,
}])];
let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], hop_hints, 10_000, 42);
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
.with_features(InvoiceFeatures::known())
.with_route_hints(hop_hints);
let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000, 42);
assert_eq!(route.paths[0][1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap());

route.paths[0][1].fee_msat = 10_000_000; // Overshoot the last channel's value
Expand Down
9 changes: 9 additions & 0 deletions lightning/src/routing/network_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,15 @@ impl NetworkGraph {
}
}

/// Clears the `NodeAnnouncementInfo` field for all nodes in the `NetworkGraph` for testing
/// purposes.
#[cfg(test)]
pub fn clear_nodes_announcement_info(&self) {
for node in self.nodes.write().unwrap().iter_mut() {
node.1.announcement_info = None;
}
}

/// For an already known node (from channel announcements), update its stored properties from a
/// given node announcement.
///
Expand Down
Loading