Include maybe_announced field in RouteHop

When sending preflight probes, we want to exclude last hops that are
possibly announced. To this end, we here include a new field in
`RouteHop` that will be `true` when we either def. know the hop to be
announced, or, if there exist public channels between the hop's
counterparties that this hop might refer to (i.e., be an alias for).
This commit is contained in:
Elias Rohrer 2023-09-12 15:51:37 +02:00
parent 758679af84
commit c6a1a12aca
No known key found for this signature in database
GPG key ID: 36153082BDF676FD
8 changed files with 86 additions and 16 deletions

View file

@ -375,6 +375,7 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p
channel_features: dest.channel_features(),
fee_msat: amt,
cltv_expiry_delta: 200,
maybe_announced_channel: true,
}], blinded_tail: None }],
route_params: None,
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
@ -409,6 +410,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
channel_features: middle.channel_features(),
fee_msat: first_hop_fee,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}, RouteHop {
pubkey: dest.get_our_node_id(),
node_features: dest.node_features(),
@ -416,6 +418,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
channel_features: dest.channel_features(),
fee_msat: amt,
cltv_expiry_delta: 200,
maybe_announced_channel: true,
}], blinded_tail: None }],
route_params: None,
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {

View file

@ -1685,6 +1685,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA as u32,
maybe_announced_channel: true,
}], blinded_tail: None };
$nodes[0].scorer.lock().unwrap().expect(TestResult::PaymentFailure { path: path.clone(), short_channel_id: scored_scid });

View file

@ -1036,7 +1036,8 @@ fn fake_network_test() {
short_channel_id: chan_2.0.contents.short_channel_id,
channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_3.0.contents.cltv_expiry_delta as u32
cltv_expiry_delta: chan_3.0.contents.cltv_expiry_delta as u32,
maybe_announced_channel: true,
});
hops.push(RouteHop {
pubkey: nodes[3].node.get_our_node_id(),
@ -1044,7 +1045,8 @@ fn fake_network_test() {
short_channel_id: chan_3.0.contents.short_channel_id,
channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_4.1.contents.cltv_expiry_delta as u32
cltv_expiry_delta: chan_4.1.contents.cltv_expiry_delta as u32,
maybe_announced_channel: true,
});
hops.push(RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
@ -1053,6 +1055,7 @@ fn fake_network_test() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 1000000,
cltv_expiry_delta: TEST_FINAL_CLTV,
maybe_announced_channel: true,
});
hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
hops[0].fee_msat = chan_3.0.contents.fee_base_msat as u64 + chan_3.0.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
@ -1067,7 +1070,8 @@ fn fake_network_test() {
short_channel_id: chan_4.0.contents.short_channel_id,
channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_3.1.contents.cltv_expiry_delta as u32
cltv_expiry_delta: chan_3.1.contents.cltv_expiry_delta as u32,
maybe_announced_channel: true,
});
hops.push(RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
@ -1075,7 +1079,8 @@ fn fake_network_test() {
short_channel_id: chan_3.0.contents.short_channel_id,
channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: chan_2.1.contents.cltv_expiry_delta as u32
cltv_expiry_delta: chan_2.1.contents.cltv_expiry_delta as u32,
maybe_announced_channel: true,
});
hops.push(RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
@ -1084,6 +1089,7 @@ fn fake_network_test() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 1000000,
cltv_expiry_delta: TEST_FINAL_CLTV,
maybe_announced_channel: true,
});
hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;

View file

@ -984,27 +984,27 @@ mod tests {
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
], blinded_tail: None }],
route_params: None,

View file

@ -1853,6 +1853,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: 0,
maybe_announced_channel: true,
}], blinded_tail: None }],
route_params: Some(route_params.clone()),
};
@ -2153,6 +2154,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: invoice.amount_msats(),
cltv_expiry_delta: 0,
maybe_announced_channel: true,
}
],
blinded_tail: None,

View file

@ -2201,6 +2201,7 @@ fn auto_retry_partial_failure() {
channel_features: nodes[1].node.channel_features(),
fee_msat: amt_msat / 2,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
Path { hops: vec![RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
@ -2209,6 +2210,7 @@ fn auto_retry_partial_failure() {
channel_features: nodes[1].node.channel_features(),
fee_msat: amt_msat / 2,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
],
route_params: Some(route_params.clone()),
@ -2222,6 +2224,7 @@ fn auto_retry_partial_failure() {
channel_features: nodes[1].node.channel_features(),
fee_msat: amt_msat / 4,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
Path { hops: vec![RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
@ -2230,6 +2233,7 @@ fn auto_retry_partial_failure() {
channel_features: nodes[1].node.channel_features(),
fee_msat: amt_msat / 4,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
],
route_params: Some(route_params.clone()),
@ -2243,6 +2247,7 @@ fn auto_retry_partial_failure() {
channel_features: nodes[1].node.channel_features(),
fee_msat: amt_msat / 4,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
],
route_params: Some(route_params.clone()),
@ -2487,6 +2492,7 @@ fn retry_multi_path_single_failed_payment() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 10_000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
Path { hops: vec![RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
@ -2495,6 +2501,7 @@ fn retry_multi_path_single_failed_payment() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 100_000_001, // Our default max-HTLC-value is 10% of the channel value, which this is one more than
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
],
route_params: Some(route_params.clone()),
@ -2576,6 +2583,7 @@ fn immediate_retry_on_failure() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 100_000_001, // Our default max-HTLC-value is 10% of the channel value, which this is one more than
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
],
route_params: Some(RouteParameters::from_payment_params_and_value(
@ -2662,6 +2670,7 @@ fn no_extra_retries_on_back_to_back_fail() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 0, // nodes[1] will fail the payment as we don't pay its fee
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}, RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
node_features: nodes[2].node.node_features(),
@ -2669,6 +2678,7 @@ fn no_extra_retries_on_back_to_back_fail() {
channel_features: nodes[2].node.channel_features(),
fee_msat: 100_000_000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
Path { hops: vec![RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
@ -2677,6 +2687,7 @@ fn no_extra_retries_on_back_to_back_fail() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 0, // nodes[1] will fail the payment as we don't pay its fee
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}, RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
node_features: nodes[2].node.node_features(),
@ -2684,6 +2695,7 @@ fn no_extra_retries_on_back_to_back_fail() {
channel_features: nodes[2].node.channel_features(),
fee_msat: 100_000_000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None }
],
route_params: Some(RouteParameters::from_payment_params_and_value(
@ -2862,6 +2874,7 @@ fn test_simple_partial_retry() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 0, // nodes[1] will fail the payment as we don't pay its fee
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}, RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
node_features: nodes[2].node.node_features(),
@ -2869,6 +2882,7 @@ fn test_simple_partial_retry() {
channel_features: nodes[2].node.channel_features(),
fee_msat: 100_000_000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
Path { hops: vec![RouteHop {
pubkey: nodes[1].node.get_our_node_id(),
@ -2877,6 +2891,7 @@ fn test_simple_partial_retry() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 100_000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}, RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
node_features: nodes[2].node.node_features(),
@ -2884,6 +2899,7 @@ fn test_simple_partial_retry() {
channel_features: nodes[2].node.channel_features(),
fee_msat: 100_000_000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None }
],
route_params: Some(RouteParameters::from_payment_params_and_value(
@ -3026,6 +3042,7 @@ fn test_threaded_payment_retries() {
channel_features: nodes[1].node.channel_features(),
fee_msat: 0,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}, RouteHop {
pubkey: nodes[3].node.get_our_node_id(),
node_features: nodes[2].node.node_features(),
@ -3033,6 +3050,7 @@ fn test_threaded_payment_retries() {
channel_features: nodes[2].node.channel_features(),
fee_msat: amt_msat / 1000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None },
Path { hops: vec![RouteHop {
pubkey: nodes[2].node.get_our_node_id(),
@ -3041,6 +3059,7 @@ fn test_threaded_payment_retries() {
channel_features: nodes[2].node.channel_features(),
fee_msat: 100_000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}, RouteHop {
pubkey: nodes[3].node.get_our_node_id(),
node_features: nodes[3].node.node_features(),
@ -3048,6 +3067,7 @@ fn test_threaded_payment_retries() {
channel_features: nodes[3].node.channel_features(),
fee_msat: amt_msat - amt_msat / 1000,
cltv_expiry_delta: 100,
maybe_announced_channel: true,
}], blinded_tail: None }
],
route_params: Some(RouteParameters::from_payment_params_and_value(

View file

@ -250,10 +250,20 @@ pub struct RouteHop {
///
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
pub cltv_expiry_delta: u32,
/// Indicates whether this hop is possibly announced in the public network graph.
///
/// Will be `true` if there is a possibility that the channel is publicly known, i.e., if we
/// either know for sure it's announced in the public graph, or if any public channels exist
/// for which the given `short_channel_id` could be an alias for. Will be `false` if we believe
/// the channel to be unannounced.
///
/// Will be `true` for objects serialized with LDK version 0.0.116 and before.
pub maybe_announced_channel: bool,
}
impl_writeable_tlv_based!(RouteHop, {
(0, pubkey, required),
(1, maybe_announced_channel, (default_value, true)),
(2, node_features, required),
(4, short_channel_id, required),
(6, channel_features, required),
@ -2472,9 +2482,27 @@ where L::Target: Logger {
let mut paths = Vec::new();
for payment_path in selected_route {
let mut hops = Vec::with_capacity(payment_path.hops.len());
let mut prev_hop_node_id = our_node_id;
for (hop, node_features) in payment_path.hops.iter()
.filter(|(h, _)| h.candidate.short_channel_id().is_some())
{
let maybe_announced_channel = if let CandidateRouteHop::PublicHop { .. } = hop.candidate {
// If we sourced the hop from the graph we're sure the target node is announced.
true
} else if let CandidateRouteHop::FirstHop { details } = hop.candidate {
// If this is a first hop we also know if it's announced.
details.is_public
} else {
// If we sourced it any other way, we double-check the network graph to see if
// there are announced channels between the endpoints. If so, the hop might be
// referring to any of the announced channels, as its `short_channel_id` might be
// an alias, in which case we don't take any chances here.
network_graph.node(&hop.node_id).map_or(false, |hop_node|
hop_node.channels.iter().any(|scid| network_graph.channel(*scid)
.map_or(false, |c| c.as_directed_from(&prev_hop_node_id).is_some()))
)
};
hops.push(RouteHop {
pubkey: PublicKey::from_slice(hop.node_id.as_slice()).map_err(|_| LightningError{err: format!("Public key {:?} is invalid", &hop.node_id), action: ErrorAction::IgnoreAndLog(Level::Trace)})?,
node_features: node_features.clone(),
@ -2482,7 +2510,10 @@ where L::Target: Logger {
channel_features: hop.candidate.features(),
fee_msat: hop.fee_msat,
cltv_expiry_delta: hop.candidate.cltv_expiry_delta(),
maybe_announced_channel,
});
prev_hop_node_id = hop.node_id;
}
let mut final_cltv_delta = final_cltv_expiry_delta;
let blinded_tail = payment_path.hops.last().and_then(|(h, _)| {
@ -5964,17 +5995,17 @@ mod tests {
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0, maybe_announced_channel: true,
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0, maybe_announced_channel: true,
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0, maybe_announced_channel: true,
},
], blinded_tail: None }],
route_params: None,
@ -5991,23 +6022,23 @@ mod tests {
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0, maybe_announced_channel: true,
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0, maybe_announced_channel: true,
},
], blinded_tail: None }, Path { hops: vec![
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0, maybe_announced_channel: true,
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0, maybe_announced_channel: true,
},
], blinded_tail: None }],
route_params: None,
@ -6606,6 +6637,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 100,
cltv_expiry_delta: 0,
maybe_announced_channel: true,
}],
blinded_tail: Some(BlindedTail {
hops: blinded_path_1.blinded_hops,
@ -6620,6 +6652,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 100,
cltv_expiry_delta: 0,
maybe_announced_channel: true,
}], blinded_tail: None }],
route_params: None,
};
@ -6659,6 +6692,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 100,
cltv_expiry_delta: 0,
maybe_announced_channel: false,
},
RouteHop {
pubkey: blinded_path.introduction_node_id,
@ -6667,6 +6701,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 1,
cltv_expiry_delta: 0,
maybe_announced_channel: false,
}],
blinded_tail: Some(BlindedTail {
hops: blinded_path.blinded_hops,
@ -6699,6 +6734,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 100,
cltv_expiry_delta: 0,
maybe_announced_channel: false,
},
RouteHop {
pubkey: blinded_path.introduction_node_id,
@ -6707,6 +6743,7 @@ mod tests {
channel_features: ChannelFeatures::empty(),
fee_msat: 1,
cltv_expiry_delta: 0,
maybe_announced_channel: false,
}
],
blinded_tail: Some(BlindedTail {

View file

@ -2014,6 +2014,7 @@ mod tests {
channel_features: channelmanager::provided_channel_features(&config),
fee_msat,
cltv_expiry_delta: 18,
maybe_announced_channel: true,
}
}