Skip to content

Commit ba48ac8

Browse files
committed
Allow building of a route from given hops.
Implements `build_route_from_hops`, which provides a simple way to build a route from us (payer) with the given hops ending at the target node (payee). This may be useful, e.g., for probing the chosen path.
1 parent aac3907 commit ba48ac8

File tree

1 file changed

+94
-15
lines changed

1 file changed

+94
-15
lines changed

lightning/src/routing/router.rs

+94-15
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
1919
use ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
2020
use routing::scoring::{ChannelUsage, Score};
2121
use routing::network_graph::{DirectedChannelInfoWithUpdate, EffectiveCapacity, NetworkGraph, ReadOnlyNetworkGraph, NodeId, RoutingFees};
22-
use util::ser::{Writeable, Readable};
22+
use util::ser::{Writeable, Readable, Writer};
2323
use util::logger::{Level, Logger};
2424
use util::chacha20::ChaCha20;
2525

@@ -151,8 +151,8 @@ impl Readable for Route {
151151

152152
/// Parameters needed to find a [`Route`].
153153
///
154-
/// Passed to [`find_route`] and also provided in [`Event::PaymentPathFailed`] for retrying a failed
155-
/// payment path.
154+
/// Passed to [`find_route`] and [`build_route_from_hops`], but also provided in
155+
/// [`Event::PaymentPathFailed`] for retrying a failed payment path.
156156
///
157157
/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
158158
#[derive(Clone, Debug)]
@@ -676,16 +676,11 @@ pub fn find_route<L: Deref, S: Score>(
676676
) -> Result<Route, LightningError>
677677
where L::Target: Logger {
678678
let network_graph = network.read_only();
679-
match get_route(
680-
our_node_pubkey, &route_params.payment_params, &network_graph, first_hops, route_params.final_value_msat,
681-
route_params.final_cltv_expiry_delta, logger, scorer, random_seed_bytes
682-
) {
683-
Ok(mut route) => {
684-
add_random_cltv_offset(&mut route, &route_params.payment_params, &network_graph, random_seed_bytes);
685-
Ok(route)
686-
},
687-
Err(err) => Err(err),
688-
}
679+
let mut route = get_route(our_node_pubkey, &route_params.payment_params, &network_graph, first_hops,
680+
route_params.final_value_msat, route_params.final_cltv_expiry_delta, logger, scorer,
681+
random_seed_bytes)?;
682+
add_random_cltv_offset(&mut route, &route_params.payment_params, &network_graph, random_seed_bytes);
683+
Ok(route)
689684
}
690685

691686
pub(crate) fn get_route<L: Deref, S: Score>(
@@ -1703,7 +1698,9 @@ where L::Target: Logger {
17031698
// destination, if the remaining CLTV expiry delta exactly matches a feasible path in the network
17041699
// graph. In order to improve privacy, this method obfuscates the CLTV expiry deltas along the
17051700
// payment path by adding a randomized 'shadow route' offset to the final hop.
1706-
fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph, random_seed_bytes: &[u8; 32]) {
1701+
fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters,
1702+
network_graph: &ReadOnlyNetworkGraph, random_seed_bytes: &[u8; 32]
1703+
) {
17071704
let network_channels = network_graph.channels();
17081705
let network_nodes = network_graph.nodes();
17091706

@@ -1785,10 +1782,72 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters,
17851782
}
17861783
}
17871784

1785+
/// Build a route from us (payer) with the given hops ending at the target node (payee), which
1786+
/// may be used to simply probe the given path.
1787+
///
1788+
/// Re-uses logic from `find_route`, so the restrictions described there also apply here.
1789+
pub fn build_route_from_hops<L: Deref>(
1790+
our_node_pubkey: &PublicKey, hops: &[PublicKey], route_params: &RouteParameters, network: &NetworkGraph,
1791+
logger: L, random_seed_bytes: &[u8; 32]
1792+
) -> Result<Route, LightningError>
1793+
where L::Target: Logger {
1794+
let network_graph = network.read_only();
1795+
let mut route = build_route_from_hops_internal(
1796+
our_node_pubkey, hops, &route_params.payment_params, &network_graph,
1797+
route_params.final_value_msat, route_params.final_cltv_expiry_delta, logger, random_seed_bytes)?;
1798+
add_random_cltv_offset(&mut route, &route_params.payment_params, &network_graph, random_seed_bytes);
1799+
Ok(route)
1800+
}
1801+
1802+
fn build_route_from_hops_internal<L: Deref>(
1803+
our_node_pubkey: &PublicKey, hops: &[PublicKey], payment_params: &PaymentParameters,
1804+
network_graph: &ReadOnlyNetworkGraph, final_value_msat: u64, final_cltv_expiry_delta: u32,
1805+
logger: L, random_seed_bytes: &[u8; 32]
1806+
) -> Result<Route, LightningError> where L::Target: Logger {
1807+
1808+
struct HopScorer<'a> {
1809+
our_node_pubkey: &'a PublicKey,
1810+
hops: &'a [PublicKey],
1811+
}
1812+
1813+
impl Score for HopScorer<'_> {
1814+
fn channel_penalty_msat(&self, _short_channel_id: u64, source: &NodeId, target: &NodeId,
1815+
_usage: ChannelUsage) -> u64
1816+
{
1817+
let mut cur_id = NodeId::from_pubkey(self.our_node_pubkey);
1818+
for i in 0..self.hops.len() {
1819+
let next_id = NodeId::from_pubkey(&self.hops[i]);
1820+
if cur_id == *source && next_id == *target {
1821+
return 0;
1822+
}
1823+
cur_id = next_id;
1824+
}
1825+
u64::max_value()
1826+
}
1827+
1828+
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
1829+
1830+
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
1831+
}
1832+
1833+
impl<'a> Writeable for HopScorer<'a> {
1834+
#[inline]
1835+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
1836+
write_tlv_fields!(w, {});
1837+
Ok(())
1838+
}
1839+
}
1840+
1841+
let scorer = HopScorer { our_node_pubkey, hops };
1842+
1843+
get_route(our_node_pubkey, payment_params, network_graph, None, final_value_msat,
1844+
final_cltv_expiry_delta, logger, &scorer, random_seed_bytes)
1845+
}
1846+
17881847
#[cfg(test)]
17891848
mod tests {
17901849
use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId};
1791-
use routing::router::{get_route, add_random_cltv_offset, default_node_features,
1850+
use routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
17921851
PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees,
17931852
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE};
17941853
use routing::scoring::{ChannelUsage, Score};
@@ -5486,6 +5545,26 @@ mod tests {
54865545
assert!(path_plausibility.iter().all(|x| *x));
54875546
}
54885547

5548+
#[test]
5549+
fn builds_correct_path_from_hops() {
5550+
let (secp_ctx, network, _, _, logger) = build_graph();
5551+
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
5552+
let network_graph = network.read_only();
5553+
5554+
let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
5555+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
5556+
5557+
let payment_params = PaymentParameters::from_node_id(nodes[3]);
5558+
let hops = [nodes[1], nodes[2], nodes[4], nodes[3]];
5559+
let route = build_route_from_hops_internal(&our_id, &hops, &payment_params,
5560+
&network_graph, 100, 0, Arc::clone(&logger), &random_seed_bytes).unwrap();
5561+
let route_hop_pubkeys = route.paths[0].iter().map(|hop| hop.pubkey).collect::<Vec<_>>();
5562+
assert_eq!(hops.len(), route.paths[0].len());
5563+
for (idx, hop_pubkey) in hops.iter().enumerate() {
5564+
assert!(*hop_pubkey == route_hop_pubkeys[idx]);
5565+
}
5566+
}
5567+
54895568
#[cfg(not(feature = "no-std"))]
54905569
pub(super) fn random_init_seed() -> u64 {
54915570
// Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.

0 commit comments

Comments
 (0)