+/// Construct a route from us (payer) to the target node (payee) via the given hops (which should
+/// exclude the payer, but include the payee). This may be useful, e.g., for probing the chosen path.
+///
+/// Re-uses logic from `find_route`, so the restrictions described there also apply here.
+pub fn build_route_from_hops<L: Deref, GL: Deref>(
+ our_node_pubkey: &PublicKey, hops: &[PublicKey], route_params: &RouteParameters,
+ network_graph: &NetworkGraph<GL>, logger: L, random_seed_bytes: &[u8; 32]
+) -> Result<Route, LightningError>
+where L::Target: Logger, GL::Target: Logger {
+ let graph_lock = network_graph.read_only();
+ let mut route = build_route_from_hops_internal(
+ our_node_pubkey, hops, &route_params.payment_params, &graph_lock,
+ route_params.final_value_msat, route_params.final_cltv_expiry_delta, logger, random_seed_bytes)?;
+ add_random_cltv_offset(&mut route, &route_params.payment_params, &graph_lock, random_seed_bytes);
+ Ok(route)
+}
+
+fn build_route_from_hops_internal<L: Deref>(
+ our_node_pubkey: &PublicKey, hops: &[PublicKey], payment_params: &PaymentParameters,
+ network_graph: &ReadOnlyNetworkGraph, final_value_msat: u64, final_cltv_expiry_delta: u32,
+ logger: L, random_seed_bytes: &[u8; 32]
+) -> Result<Route, LightningError> where L::Target: Logger {
+
+ struct HopScorer {
+ our_node_id: NodeId,
+ hop_ids: [Option<NodeId>; MAX_PATH_LENGTH_ESTIMATE as usize],
+ }
+
+ impl Score for HopScorer {
+ fn channel_penalty_msat(&self, _short_channel_id: u64, source: &NodeId, target: &NodeId,
+ _usage: ChannelUsage) -> u64
+ {
+ let mut cur_id = self.our_node_id;
+ for i in 0..self.hop_ids.len() {
+ if let Some(next_id) = self.hop_ids[i] {
+ if cur_id == *source && next_id == *target {
+ return 0;
+ }
+ cur_id = next_id;
+ } else {
+ break;
+ }
+ }
+ u64::max_value()
+ }
+
+ fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+
+ fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
+ }
+
+ impl<'a> Writeable for HopScorer {
+ #[inline]
+ fn write<W: Writer>(&self, _w: &mut W) -> Result<(), io::Error> {
+ unreachable!();
+ }
+ }
+
+ if hops.len() > MAX_PATH_LENGTH_ESTIMATE.into() {
+ return Err(LightningError{err: "Cannot build a route exceeding the maximum path length.".to_owned(), action: ErrorAction::IgnoreError});
+ }
+
+ let our_node_id = NodeId::from_pubkey(our_node_pubkey);
+ let mut hop_ids = [None; MAX_PATH_LENGTH_ESTIMATE as usize];
+ for i in 0..hops.len() {
+ hop_ids[i] = Some(NodeId::from_pubkey(&hops[i]));
+ }
+
+ let scorer = HopScorer { our_node_id, hop_ids };
+
+ get_route(our_node_pubkey, payment_params, network_graph, None, final_value_msat,
+ final_cltv_expiry_delta, logger, &scorer, random_seed_bytes)
+}
+