+
+ #[test]
+ fn honors_manual_penalties() {
+ let (secp_ctx, network_graph, _, _, logger) = build_line_graph();
+ let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+
+ let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
+ let random_seed_bytes = keys_manager.get_secure_random_bytes();
+
+ let mut scorer_params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), Arc::clone(&network_graph), Arc::clone(&logger));
+
+ // First check set manual penalties are returned by the scorer.
+ let usage = ChannelUsage {
+ amount_msat: 0,
+ inflight_htlc_msat: 0,
+ effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
+ };
+ scorer_params.set_manual_penalty(&NodeId::from_pubkey(&nodes[3]), 123);
+ scorer_params.set_manual_penalty(&NodeId::from_pubkey(&nodes[4]), 456);
+ assert_eq!(scorer.channel_penalty_msat(42, &NodeId::from_pubkey(&nodes[3]), &NodeId::from_pubkey(&nodes[4]), usage, &scorer_params), 456);
+
+ // Then check we can get a normal route
+ let payment_params = PaymentParameters::from_node_id(nodes[10], 42);
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
+ assert!(route.is_ok());
+
+ // Then check that we can't get a route if we ban an intermediate node.
+ scorer_params.add_banned(&NodeId::from_pubkey(&nodes[3]));
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
+ assert!(route.is_err());
+
+ // Finally make sure we can route again, when we remove the ban.
+ scorer_params.remove_banned(&NodeId::from_pubkey(&nodes[3]));
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
+ assert!(route.is_ok());
+ }
+
+ #[test]
+ fn blinded_route_ser() {
+ let blinded_path_1 = BlindedPath {
+ introduction_node_id: ln_test_utils::pubkey(42),
+ blinding_point: ln_test_utils::pubkey(43),
+ blinded_hops: vec![
+ BlindedHop { blinded_node_id: ln_test_utils::pubkey(44), encrypted_payload: Vec::new() },
+ BlindedHop { blinded_node_id: ln_test_utils::pubkey(45), encrypted_payload: Vec::new() }
+ ],
+ };
+ let blinded_path_2 = BlindedPath {
+ introduction_node_id: ln_test_utils::pubkey(46),
+ blinding_point: ln_test_utils::pubkey(47),
+ blinded_hops: vec![
+ BlindedHop { blinded_node_id: ln_test_utils::pubkey(48), encrypted_payload: Vec::new() },
+ BlindedHop { blinded_node_id: ln_test_utils::pubkey(49), encrypted_payload: Vec::new() }
+ ],
+ };
+ // (De)serialize a Route with 1 blinded path out of two total paths.
+ let mut route = Route { paths: vec![Path {
+ hops: vec![RouteHop {
+ pubkey: ln_test_utils::pubkey(50),
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 42,
+ channel_features: ChannelFeatures::empty(),
+ fee_msat: 100,
+ cltv_expiry_delta: 0,
+ }],
+ blinded_tail: Some(BlindedTail {
+ hops: blinded_path_1.blinded_hops,
+ blinding_point: blinded_path_1.blinding_point,
+ excess_final_cltv_expiry_delta: 40,
+ final_value_msat: 100,
+ })}, Path {
+ hops: vec![RouteHop {
+ pubkey: ln_test_utils::pubkey(51),
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 43,
+ channel_features: ChannelFeatures::empty(),
+ fee_msat: 100,
+ cltv_expiry_delta: 0,
+ }], blinded_tail: None }],
+ payment_params: None,
+ };
+ let encoded_route = route.encode();
+ let decoded_route: Route = Readable::read(&mut Cursor::new(&encoded_route[..])).unwrap();
+ assert_eq!(decoded_route.paths[0].blinded_tail, route.paths[0].blinded_tail);
+ assert_eq!(decoded_route.paths[1].blinded_tail, route.paths[1].blinded_tail);
+
+ // (De)serialize a Route with two paths, each containing a blinded tail.
+ route.paths[1].blinded_tail = Some(BlindedTail {
+ hops: blinded_path_2.blinded_hops,
+ blinding_point: blinded_path_2.blinding_point,
+ excess_final_cltv_expiry_delta: 41,
+ final_value_msat: 101,
+ });
+ let encoded_route = route.encode();
+ let decoded_route: Route = Readable::read(&mut Cursor::new(&encoded_route[..])).unwrap();
+ assert_eq!(decoded_route.paths[0].blinded_tail, route.paths[0].blinded_tail);
+ assert_eq!(decoded_route.paths[1].blinded_tail, route.paths[1].blinded_tail);
+ }
+
+ #[test]
+ fn blinded_path_inflight_processing() {
+ // Ensure we'll score the channel that's inbound to a blinded path's introduction node, and
+ // account for the blinded tail's final amount_msat.
+ let mut inflight_htlcs = InFlightHtlcs::new();
+ let blinded_path = BlindedPath {
+ introduction_node_id: ln_test_utils::pubkey(43),
+ blinding_point: ln_test_utils::pubkey(48),
+ blinded_hops: vec![BlindedHop { blinded_node_id: ln_test_utils::pubkey(49), encrypted_payload: Vec::new() }],
+ };
+ let path = Path {
+ hops: vec![RouteHop {
+ pubkey: ln_test_utils::pubkey(42),
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 42,
+ channel_features: ChannelFeatures::empty(),
+ fee_msat: 100,
+ cltv_expiry_delta: 0,
+ },
+ RouteHop {
+ pubkey: blinded_path.introduction_node_id,
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 43,
+ channel_features: ChannelFeatures::empty(),
+ fee_msat: 1,
+ cltv_expiry_delta: 0,
+ }],
+ blinded_tail: Some(BlindedTail {
+ hops: blinded_path.blinded_hops,
+ blinding_point: blinded_path.blinding_point,
+ excess_final_cltv_expiry_delta: 0,
+ final_value_msat: 200,
+ }),
+ };
+ inflight_htlcs.process_path(&path, ln_test_utils::pubkey(44));
+ assert_eq!(*inflight_htlcs.0.get(&(42, true)).unwrap(), 301);
+ assert_eq!(*inflight_htlcs.0.get(&(43, false)).unwrap(), 201);
+ }
+
+ #[test]
+ fn blinded_path_cltv_shadow_offset() {
+ // Make sure we add a shadow offset when sending to blinded paths.
+ let blinded_path = BlindedPath {
+ introduction_node_id: ln_test_utils::pubkey(43),
+ blinding_point: ln_test_utils::pubkey(44),
+ blinded_hops: vec![
+ BlindedHop { blinded_node_id: ln_test_utils::pubkey(45), encrypted_payload: Vec::new() },
+ BlindedHop { blinded_node_id: ln_test_utils::pubkey(46), encrypted_payload: Vec::new() }
+ ],
+ };
+ let mut route = Route { paths: vec![Path {
+ hops: vec![RouteHop {
+ pubkey: ln_test_utils::pubkey(42),
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 42,
+ channel_features: ChannelFeatures::empty(),
+ fee_msat: 100,
+ cltv_expiry_delta: 0,
+ },
+ RouteHop {
+ pubkey: blinded_path.introduction_node_id,
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 43,
+ channel_features: ChannelFeatures::empty(),
+ fee_msat: 1,
+ cltv_expiry_delta: 0,
+ }
+ ],
+ blinded_tail: Some(BlindedTail {
+ hops: blinded_path.blinded_hops,
+ blinding_point: blinded_path.blinding_point,
+ excess_final_cltv_expiry_delta: 0,
+ final_value_msat: 200,
+ }),
+ }], payment_params: None};
+
+ let payment_params = PaymentParameters::from_node_id(ln_test_utils::pubkey(47), 18);
+ let (_, network_graph, _, _, _) = build_line_graph();
+ add_random_cltv_offset(&mut route, &payment_params, &network_graph.read_only(), &[0; 32]);
+ assert_eq!(route.paths[0].blinded_tail.as_ref().unwrap().excess_final_cltv_expiry_delta, 40);
+ assert_eq!(route.paths[0].hops.last().unwrap().cltv_expiry_delta, 40);
+ }