more refactor

This commit is contained in:
John Smith 2023-02-27 16:59:47 -05:00
parent b3c55f4a6d
commit bb4dbb7c4a
4 changed files with 94 additions and 84 deletions

View File

@ -80,9 +80,15 @@ impl RouteSetSpecDetail {
pub fn hop_count(&self) -> usize {
self.hop_node_refs.len()
}
pub fn hop_node_ref(&self, idx: usize) -> Option<NodeRef> {
self.hop_node_refs.get(idx).cloned()
}
pub fn get_stability(&self) -> Stability {
self.stability
}
pub fn get_directions(&self) -> DirectionSet {
self.directions
}
pub fn is_sequencing_match(&self, sequencing: Sequencing) -> bool {
match sequencing {
Sequencing::NoPreference => true,

View File

@ -730,52 +730,46 @@ impl RouteSpecStore {
/// Don't pick any routes that have failed and haven't been tested yet
fn first_available_route_inner<'a>(
inner: &'a RouteSpecStoreInner,
crypto_kind: CryptoKind,
min_hop_count: usize,
max_hop_count: usize,
stability: Stability,
sequencing: Sequencing,
directions: DirectionSet,
avoid_nodes: &[TypedKey],
) -> Option<PublicKey> {
) -> Option<RouteId> {
let cur_ts = get_aligned_timestamp();
let mut routes = Vec::new();
// Get all valid routes, allow routes that need testing
// but definitely prefer routes that have been recently tested
for detail in &inner.content.details {
if detail.1.stability >= stability
&& detail.1.is_sequencing_match(sequencing)
&& detail.1.hops.len() >= min_hop_count
&& detail.1.hops.len() <= max_hop_count
&& detail.1.directions.is_superset(directions)
&& !detail.1.published
for (id, rssd) in inner.content.iter_details() {
if rssd.get_stability() >= stability
&& rssd.is_sequencing_match(sequencing)
&& rssd.hop_count() >= min_hop_count
&& rssd.hop_count() <= max_hop_count
&& rssd.get_directions().is_superset(directions)
&& rssd.get_route_set_keys().kinds().contains(&crypto_kind)
&& !rssd.is_published()
&& !rssd.contains_nodes(avoid_nodes)
{
let mut avoid = false;
for h in &detail.1.hops {
if avoid_nodes.contains(h) {
avoid = true;
break;
}
}
if !avoid {
routes.push(detail);
}
routes.push((id, rssd));
}
}
// Sort the routes by preference
routes.sort_by(|a, b| {
let a_needs_testing = a.1.stats.needs_testing(cur_ts);
let b_needs_testing = b.1.stats.needs_testing(cur_ts);
let a_needs_testing = a.1.get_stats().needs_testing(cur_ts);
let b_needs_testing = b.1.get_stats().needs_testing(cur_ts);
if !a_needs_testing && b_needs_testing {
return cmp::Ordering::Less;
}
if !b_needs_testing && a_needs_testing {
return cmp::Ordering::Greater;
}
let a_latency = a.1.stats.latency_stats().average;
let b_latency = b.1.stats.latency_stats().average;
let a_latency = a.1.get_stats().latency_stats().average;
let b_latency = b.1.get_stats().latency_stats().average;
a_latency.cmp(&b_latency)
});
@ -847,14 +841,20 @@ impl RouteSpecStore {
mut private_route: PrivateRoute,
) -> EyreResult<Option<CompiledRoute>> {
// let profile_start_ts = get_timestamp();
let inner = &mut *self.inner.lock();
let routing_table = self.unlocked_inner.routing_table.clone();
let rti = &mut *routing_table.inner.write();
let pr_pubkey = private_route.public_key;
// Get useful private route properties
let crypto_kind = private_route.crypto_kind();
let crypto = routing_table.crypto();
let Some(vcrypto) = crypto.get(crypto_kind) else {
bail!("crypto not supported for route");
};
let pr_pubkey = private_route.public_key.key;
let pr_hopcount = private_route.hop_count as usize;
let max_route_hop_count = self.unlocked_inner.max_route_hop_count;
// Check private route hop count isn't larger than the max route hop count plus one for the 'first hop' header
if pr_hopcount > (max_route_hop_count + 1) {
bail!("private route hop count too long");
@ -870,12 +870,11 @@ impl RouteSpecStore {
};
let opt_first_hop = match pr_first_hop_node {
RouteNode::NodeId(id) => rti.lookup_node_ref(routing_table.clone(), id.key),
RouteNode::NodeId(id) => rti.lookup_any_node_ref(routing_table.clone(), id),
RouteNode::PeerInfo(pi) => rti.register_node_with_peer_info(
routing_table.clone(),
RoutingDomain::PublicInternet,
pi.node_id.key,
pi.signed_node_info.clone(),
pi,
false,
),
};
@ -892,22 +891,23 @@ impl RouteSpecStore {
// Return the compiled safety route
//println!("compile_safety_route profile (stub): {} us", (get_timestamp() - profile_start_ts));
return Ok(Some(CompiledRoute {
safety_route: SafetyRoute::new_stub(routing_table.node_id(), private_route),
secret: routing_table.node_id_secret(),
safety_route: SafetyRoute::new_stub(routing_table.node_id(crypto_kind), private_route),
secret: routing_table.node_id_secret(crypto_kind),
first_hop,
}));
}
};
// If the safety route requested is also the private route, this is a loopback test, just accept it
let sr_pubkey = if safety_spec.preferred_route == Some(private_route.public_key) {
let opt_private_route_id = inner.content.get_id_by_key(&pr_pubkey);
let sr_pubkey = if opt_private_route_id.is_some() && safety_spec.preferred_route == opt_private_route_id {
// Private route is also safety route during loopback test
private_route.public_key
pr_pubkey
} else {
let Some(avoid_node_id) = private_route.first_hop_node_id() else {
bail!("compiled private route should have first hop");
};
let Some(sr_pubkey) = self.get_route_for_safety_spec_inner(inner, rti, &safety_spec, Direction::Outbound.into(), &[avoid_node_id])? else {
let Some(sr_pubkey) = self.get_route_for_safety_spec_inner(inner, rti, crypto_kind, &safety_spec, Direction::Outbound.into(), &[avoid_node_id])? else {
// No safety route could be found for this spec
return Ok(None);
};
@ -915,28 +915,33 @@ impl RouteSpecStore {
};
// Look up a few things from the safety route detail we want for the compiled route and don't borrow inner
let (optimize, first_hop, secret) = {
let safety_rsd = Self::detail(inner, &sr_pubkey).ok_or_else(|| eyre!("route missing"))?;
// We can optimize the peer info in this safety route if it has been successfully
// communicated over either via an outbound test, or used as a private route inbound
// and we are replying over the same route as our safety route outbound
let optimize = safety_rsd.stats.last_tested_ts.is_some() || safety_rsd.stats.last_received_ts.is_some();
// Get the first hop noderef of the safety route
let mut first_hop = safety_rsd.hop_node_refs.first().unwrap().clone();
// Ensure sequencing requirement is set on first hop
first_hop.set_sequencing(safety_spec.sequencing);
// Get the safety route secret key
let secret = safety_rsd.secret_key;
(optimize, first_hop, secret)
let Some(safety_route_id) = inner.content.get_id_by_key(&sr_pubkey) else {
bail!("route id missing");
};
let Some(safety_rssd) = inner.content.get_detail(&safety_route_id) else {
bail!("route set detail missing");
};
let Some(safety_rsd) = safety_rssd.get_route_by_key(&sr_pubkey) else {
bail!("route detail missing");
};
// We can optimize the peer info in this safety route if it has been successfully
// communicated over either via an outbound test, or used as a private route inbound
// and we are replying over the same route as our safety route outbound
let optimize = safety_rssd.get_stats().last_tested_ts.is_some() || safety_rssd.get_stats().last_received_ts.is_some();
// Get the first hop noderef of the safety route
let mut first_hop = safety_rssd.hop_node_ref(0).unwrap();
// Ensure sequencing requirement is set on first hop
first_hop.set_sequencing(safety_spec.sequencing);
// Get the safety route secret key
let secret = safety_rsd.secret_key;
// See if we have a cached route we can use
if optimize {
if let Some(safety_route) = self.lookup_compiled_route_cache(inner, sr_pubkey, pr_pubkey) {
if let Some(safety_route) = inner.cache.lookup_compiled_route_cache(sr_pubkey, pr_pubkey) {
// Build compiled route
let compiled_route = CompiledRoute {
safety_route,
@ -951,8 +956,6 @@ impl RouteSpecStore {
// Create hops
let hops = {
let safety_rsd = Self::detail(inner, &sr_pubkey).ok_or_else(|| eyre!("route missing"))?;
// start last blob-to-encrypt data off as private route
let mut blob_data = {
let mut pr_message = ::capnp::message::Builder::new_default();
@ -970,18 +973,17 @@ impl RouteSpecStore {
// safety route and does not include the dialInfo
// (outer hop is a RouteHopData, not a RouteHop).
// Each loop mutates 'nonce', and 'blob_data'
let mut nonce = Crypto::get_random_nonce();
let crypto = routing_table.crypto();
let mut nonce = vcrypto.random_nonce();
// Forward order (safety route), but inside-out
for h in (1..safety_rsd.hops.len()).rev() {
// Get blob to encrypt for next hop
blob_data = {
// Encrypt the previous blob ENC(nonce, DH(PKhop,SKsr))
let dh_secret = crypto
let dh_secret = vcrypto
.cached_dh(&safety_rsd.hops[h], &safety_rsd.secret_key)
.wrap_err("dh failed")?;
let enc_msg_data =
Crypto::encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)
vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)
.wrap_err("encryption failed")?;
// Make route hop data
@ -994,14 +996,14 @@ impl RouteSpecStore {
let route_hop = RouteHop {
node: if optimize {
// Optimized, no peer info, just the dht key
RouteNode::NodeId(NodeId::new(safety_rsd.hops[h]))
RouteNode::NodeId(safety_rsd.hops[h])
} else {
// Full peer info, required until we are sure the route has been fully established
let node_id = safety_rsd.hops[h];
let node_id = TypedKey::new(safety_rsd.crypto_kind, safety_rsd.hops[h]);
let pi = rti
.with_node_entry(node_id, |entry| {
entry.with(rti, |_rti, e| {
e.make_peer_info(node_id, RoutingDomain::PublicInternet)
e.make_peer_info(RoutingDomain::PublicInternet)
})
})
.flatten();
@ -1025,14 +1027,14 @@ impl RouteSpecStore {
};
// Make another nonce for the next hop
nonce = Crypto::get_random_nonce();
nonce = vcrypto.random_nonce();
}
// Encode first RouteHopData
let dh_secret = crypto
let dh_secret = vcrypto
.cached_dh(&safety_rsd.hops[0], &safety_rsd.secret_key)
.map_err(RPCError::map_internal("dh failed"))?;
let enc_msg_data = Crypto::encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)
let enc_msg_data = vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)
.map_err(RPCError::map_internal("encryption failed"))?;
let route_hop_data = RouteHopData {
@ -1045,14 +1047,14 @@ impl RouteSpecStore {
// Build safety route
let safety_route = SafetyRoute {
public_key: sr_pubkey,
public_key: TypedKey::new(crypto_kind, sr_pubkey),
hop_count: safety_spec.hop_count as u8,
hops,
};
// Add to cache but only if we have an optimized route
if optimize {
self.add_to_compiled_route_cache(inner, pr_pubkey, safety_route.clone());
inner.cache.add_to_compiled_route_cache( pr_pubkey, safety_route.clone());
}
// Build compiled route
@ -1090,7 +1092,7 @@ impl RouteSpecStore {
// See if the preferred route is here
if let Some(preferred_route) = safety_spec.preferred_route {
if let Some(preferred_rssd) = inner.content.get_detail(&preferred_route) {
// Only use the preferred route if it has the desire crypto kind
// Only use the preferred route if it has the desired crypto kind
if let Some(preferred_key) = preferred_rssd.get_route_set_keys().get(crypto_kind) {
// Only use the preferred route if it doesn't contain the avoid nodes
if !preferred_rssd.contains_nodes(avoid_nodes) {
@ -1101,8 +1103,9 @@ impl RouteSpecStore {
}
// Select a safety route from the pool or make one if we don't have one that matches
let sr_pubkey = if let Some(sr_pubkey) = Self::first_available_route_inner(
let sr_route_id = if let Some(sr_route_id) = Self::first_available_route_inner(
inner,
crypto_kind,
safety_spec.hop_count,
safety_spec.hop_count,
safety_spec.stability,
@ -1111,14 +1114,14 @@ impl RouteSpecStore {
avoid_nodes,
) {
// Found a route to use
sr_pubkey
sr_route_id
} else {
// No route found, gotta allocate one
let sr_pubkey = match self
let Some(sr_route_id) = self
.allocate_route_inner(
inner,
rti,
crypto_kind, ???
&[crypto_kind],
safety_spec.stability,
safety_spec.sequencing,
safety_spec.hop_count,
@ -1126,12 +1129,14 @@ impl RouteSpecStore {
avoid_nodes,
)
.map_err(RPCError::internal)?
{
Some(pk) => pk,
None => return Ok(None),
else {
return Ok(None);
};
sr_pubkey
sr_route_id
};
let sr_pubkey = inner.content.get_detail(&sr_route_id).unwrap().get_route_set_keys().get(crypto_kind).unwrap().key;
Ok(Some(sr_pubkey))
}
@ -1366,7 +1371,6 @@ impl RouteSpecStore {
pub fn get_route_id_for_key(&self, key: &PublicKey) -> Option<RouteId>
{
let inner = &mut *self.inner.lock();
// Check for local route
if let Some(id) = inner.content.get_id_by_key(key) {
return Some(id);

View File

@ -162,9 +162,9 @@ impl RPCProcessor {
}
SafetySelection::Safe(safety_spec) => {
// Sent directly but with a safety route, respond to private route
let ck = target.best_node_id().kind;
let crypto_kind = target.best_node_id().kind;
let Some(pr_key) = rss
.get_private_route_for_safety_spec(ck, safety_spec, &target.node_ids())
.get_private_route_for_safety_spec(crypto_kind, safety_spec, &target.node_ids())
.map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
};
@ -188,12 +188,12 @@ impl RPCProcessor {
}
SafetySelection::Safe(safety_spec) => {
// Sent via a relay but with a safety route, respond to private route
let ck = target.best_node_id().kind;
let crypto_kind = target.best_node_id().kind;
let mut avoid_nodes = relay.node_ids();
avoid_nodes.add_all(&target.node_ids());
let Some(pr_key) = rss
.get_private_route_for_safety_spec(ck, safety_spec, &avoid_nodes)
.get_private_route_for_safety_spec(crypto_kind, safety_spec, &avoid_nodes)
.map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
};
@ -246,7 +246,7 @@ impl RPCProcessor {
// Check for loopback test
let opt_private_route_id = rss.get_route_id_for_key(&private_route.public_key.key);
let pr_key = if safety_spec.preferred_route == opt_private_route_id
let pr_key = if opt_private_route_id.is_some() && safety_spec.preferred_route == opt_private_route_id
{
// Private route is also safety route during loopback test
private_route.public_key.key

View File

@ -192,13 +192,13 @@ impl RPCProcessor {
&routed_operation.signatures,
&routed_operation.data,
sender_id,
|rsd| {
|rssd, rsd| {
(
rsd.secret_key,
SafetySpec {
preferred_route: Some(pr_pubkey),
hop_count: rsd.hop_count(),
stability: rsd.get_stability(),
preferred_route: rss.get_route_id_for_key(&pr_pubkey),
hop_count: rssd.hop_count(),
stability: rssd.get_stability(),
sequencing: routed_operation.sequencing,
},
)