private route work
This commit is contained in:
@@ -4,9 +4,14 @@ use serde::*;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
struct RouteSpecDetail {
|
||||
/// The actual route spec
|
||||
#[serde(with = "arc_serialize")]
|
||||
route_spec: Arc<RouteSpec>,
|
||||
/// Secret key
|
||||
#[serde(skip)]
|
||||
secret_key: DHTKeySecret,
|
||||
/// Route hops
|
||||
hops: Vec<DHTKey>,
|
||||
/// Route noderefs
|
||||
#[serde(skip)]
|
||||
hop_node_refs: Vec<NodeRef>,
|
||||
/// Transfers up and down
|
||||
transfer_stats_down_up: TransferStatsDownUp,
|
||||
/// Latency stats
|
||||
@@ -18,10 +23,15 @@ struct RouteSpecDetail {
|
||||
#[serde(skip)]
|
||||
transfer_stats_accounting: TransferStatsAccounting,
|
||||
/// Published private route, do not reuse for ephemeral routes
|
||||
/// Not serialized because all routes should be re-published when restarting
|
||||
#[serde(skip)]
|
||||
published: bool,
|
||||
/// Timestamp of when the route was created
|
||||
timestamp: u64,
|
||||
created_ts: u64,
|
||||
/// Timestamp of when the route was last checked for validity
|
||||
last_checked_ts: Option<u64>,
|
||||
/// Directions this route is guaranteed to work in
|
||||
directions: DirectionSet,
|
||||
}
|
||||
|
||||
/// The core representation of the RouteSpecStore that can be serialized
|
||||
@@ -34,10 +44,6 @@ pub struct RouteSpecStoreContent {
|
||||
/// Ephemeral data used to help the RouteSpecStore operate efficiently
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RouteSpecStoreCache {
|
||||
/// The fastest routes by latency
|
||||
fastest_routes: Vec<DHTKey>,
|
||||
/// The most reliable routes by node lifetime longevity
|
||||
reliable_routes: Vec<DHTKey>,
|
||||
/// How many times nodes have been used
|
||||
used_nodes: HashMap<DHTKey, usize>,
|
||||
/// How many times nodes have been used at the terminal point of a route
|
||||
@@ -62,14 +68,76 @@ fn route_spec_to_hop_cache(spec: Arc<RouteSpec>) -> Vec<u8> {
|
||||
cache
|
||||
}
|
||||
|
||||
fn node_sublist_to_hop_cache(
|
||||
nodes: &[(DHTKey, Arc<BucketEntry>)],
|
||||
start: usize,
|
||||
len: usize,
|
||||
) -> Vec<u8> {
|
||||
let mut cache: Vec<u8> = Vec::with_capacity(len * DHT_KEY_LENGTH);
|
||||
for node in &nodes[start..start + len] {
|
||||
cache.extend_from_slice(&node.0.bytes)
|
||||
/// number of route permutations is the number of unique orderings
|
||||
/// for a set of nodes, given that the first node is fixed
|
||||
fn get_route_permutation_count(hop_count: usize) -> usize {
|
||||
if hop_count == 0 {
|
||||
unreachable!();
|
||||
}
|
||||
// a single node or two nodes is always fixed
|
||||
if hop_count == 1 || hop_count == 2 {
|
||||
return 1;
|
||||
}
|
||||
// more than two nodes has factorial permutation
|
||||
// hop_count = 3 -> 2! -> 2
|
||||
// hop_count = 4 -> 3! -> 6
|
||||
(3..hop_count).into_iter().fold(2usize, |acc, x| acc * x)
|
||||
}
|
||||
|
||||
/// get the route permutation at particular 'perm' index, starting at the 'start' index
|
||||
/// for a set of 'hop_count' nodes. the first node is always fixed, and the maximum
|
||||
/// number of permutations is given by get_route_permutation_count()
|
||||
fn with_route_permutations<F>(hop_count: usize, start: usize, f: F) -> bool
|
||||
where
|
||||
F: FnMut(&[usize]) -> bool,
|
||||
{
|
||||
if hop_count == 0 {
|
||||
unreachable!();
|
||||
}
|
||||
// initial permutation
|
||||
let mut permutation: Vec<usize> = Vec::with_capacity(hop_count);
|
||||
for n in 0..hop_count {
|
||||
permutation[n] = start + n;
|
||||
}
|
||||
// if we have one hop or two, then there's only one permutation
|
||||
if hop_count == 1 || hop_count == 2 {
|
||||
return f(&permutation);
|
||||
}
|
||||
|
||||
// heaps algorithm
|
||||
fn heaps_permutation<F>(permutation: &mut [usize], size: usize, f: F) -> bool
|
||||
where
|
||||
F: FnMut(&[usize]) -> bool,
|
||||
{
|
||||
if size == 1 {
|
||||
if f(&permutation) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..size {
|
||||
if heaps_permutation(permutation, size - 1, f) {
|
||||
return true;
|
||||
}
|
||||
if size % 2 == 1 {
|
||||
permutation.swap(1, size);
|
||||
} else {
|
||||
permutation.swap(1 + i, size);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// recurse
|
||||
heaps_permutation(&mut permutation, hop_count - 1, f)
|
||||
}
|
||||
|
||||
/// get the hop cache key for a particular route permutation
|
||||
fn route_permutation_to_hop_cache(nodes: &[(DHTKey, NodeInfo)], perm: &[usize]) -> Vec<u8> {
|
||||
let mut cache: Vec<u8> = Vec::with_capacity(perm.len() * DHT_KEY_LENGTH);
|
||||
for n in perm {
|
||||
cache.extend_from_slice(&nodes[*n].0.bytes)
|
||||
}
|
||||
cache
|
||||
}
|
||||
@@ -84,30 +152,33 @@ impl RouteSpecStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_cbor(
|
||||
routing_table: RoutingTable,
|
||||
cbor: &[u8],
|
||||
) -> Result<RouteSpecStore, VeilidAPIError> {
|
||||
pub fn load(routing_table: RoutingTable) -> Result<RouteSpecStore, VeilidAPIError> {
|
||||
// Get cbor blob from table store
|
||||
let content: RouteSpecStoreContent = serde_cbor::from_slice(cbor)
|
||||
.map_err(|e| VeilidAPIError::parse_error("invalid route spec store content", e))?;
|
||||
let rss = RouteSpecStore {
|
||||
content,
|
||||
cache: Default::default(),
|
||||
};
|
||||
rss.rebuild_cache();
|
||||
rss.rebuild_cache(routing_table);
|
||||
Ok(rss)
|
||||
}
|
||||
|
||||
pub fn to_cbor(&self) -> Vec<u8> {
|
||||
serde_cbor::to_vec(&self.content).unwrap()
|
||||
pub fn save(&self, routing_table: RoutingTable) -> Result<(), VeilidAPIError> {
|
||||
// Save all the fields we care about to the cbor blob in table storage
|
||||
let cbor = serde_cbor::to_vec(&self.content).unwrap();
|
||||
let table_store = routing_table.network_manager().table_store();
|
||||
table_store.open("")
|
||||
}
|
||||
|
||||
fn rebuild_cache(&mut self) {
|
||||
fn rebuild_cache(&mut self, routing_table: RoutingTable) {
|
||||
//
|
||||
// xxx also load secrets from pstore
|
||||
let pstore = routing_table.network_manager().protected_store();
|
||||
}
|
||||
|
||||
fn detail_mut(&mut self, spec: Arc<RouteSpec>) -> &mut RouteSpecDetail {
|
||||
self.content.details.get_mut(&spec.public_key).unwrap()
|
||||
fn detail_mut(&mut self, public_key: DHTKey) -> &mut RouteSpecDetail {
|
||||
self.content.details.get_mut(&public_key).unwrap()
|
||||
}
|
||||
|
||||
/// Create a new route
|
||||
@@ -119,7 +190,8 @@ impl RouteSpecStore {
|
||||
routing_table: RoutingTable,
|
||||
reliable: bool,
|
||||
hop_count: usize,
|
||||
) -> Option<Arc<RouteSpec>> {
|
||||
directions: DirectionSet,
|
||||
) -> Option<DHTKey> {
|
||||
use core::cmp::Ordering;
|
||||
|
||||
let max_route_hop_count = {
|
||||
@@ -257,51 +329,101 @@ impl RouteSpecStore {
|
||||
}
|
||||
|
||||
// Now go through nodes and try to build a route we haven't seen yet
|
||||
let mut route_nodes = None;
|
||||
let mut route_nodes: Vec<usize> = Vec::with_capacity(hop_count);
|
||||
for start in 0..(nodes.len() - hop_count) {
|
||||
// Get the route cache key
|
||||
let key = node_sublist_to_hop_cache(&nodes, start, hop_count);
|
||||
// Try the permutations available starting with 'start'
|
||||
let done = with_route_permutations(hop_count, start, |permutation: &[usize]| {
|
||||
// Get the route cache key
|
||||
let key = route_permutation_to_hop_cache(&nodes, permutation);
|
||||
|
||||
// try each route until we find a unique one
|
||||
if !self.cache.hop_cache.contains(&key) {
|
||||
route_nodes = Some(&nodes[start..start + hop_count]);
|
||||
// Skip routes we have already seen
|
||||
if self.cache.hop_cache.contains(&key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure this route is viable by checking that each node can contact the next one
|
||||
if directions.contains(Direction::Outbound) {
|
||||
let our_node_info =
|
||||
routing_table.get_own_node_info(RoutingDomain::PublicInternet);
|
||||
let mut previous_node_info = &our_node_info;
|
||||
let mut reachable = true;
|
||||
for n in permutation {
|
||||
let current_node_info = &nodes.get(*n).as_ref().unwrap().1;
|
||||
let cm = NetworkManager::get_node_contact_method(
|
||||
previous_node_info,
|
||||
current_node_info,
|
||||
);
|
||||
if matches!(cm, ContactMethod::Unreachable) {
|
||||
reachable = false;
|
||||
break;
|
||||
}
|
||||
previous_node_info = current_node_info;
|
||||
}
|
||||
if !reachable {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if directions.contains(Direction::Inbound) {
|
||||
let our_node_info =
|
||||
routing_table.get_own_node_info(RoutingDomain::PublicInternet);
|
||||
let mut next_node_info = &our_node_info;
|
||||
let mut reachable = true;
|
||||
for n in permutation.iter().rev() {
|
||||
let current_node_info = &nodes.get(*n).as_ref().unwrap().1;
|
||||
let cm = NetworkManager::get_node_contact_method(
|
||||
current_node_info,
|
||||
next_node_info,
|
||||
);
|
||||
if matches!(cm, ContactMethod::Unreachable) {
|
||||
reachable = false;
|
||||
break;
|
||||
}
|
||||
next_node_info = current_node_info;
|
||||
}
|
||||
if !reachable {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Keep this route
|
||||
route_nodes = permutation.to_vec();
|
||||
true
|
||||
});
|
||||
if done {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if route_nodes.is_none() {
|
||||
if route_nodes.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let route_node = route_nodes.unwrap();
|
||||
|
||||
// Got a unique route, lets build the detail, register it, and return it
|
||||
let hops: Vec<RouteHopSpec> = route_node
|
||||
.into_iter()
|
||||
.map(|v| RouteHopSpec {
|
||||
dial_info: NodeDialInfo {
|
||||
node_id: NodeId::new(v.0),
|
||||
dial_info: xxx,
|
||||
},
|
||||
})
|
||||
let hops = route_nodes.iter().map(|v| nodes[*v].0).collect();
|
||||
let hop_node_refs = route_nodes
|
||||
.iter()
|
||||
.map(|v| routing_table.lookup_node_ref(nodes[*v].0).unwrap())
|
||||
.collect();
|
||||
|
||||
let (public_key, secret_key) = generate_secret();
|
||||
let route_spec = Arc::new(RouteSpec {
|
||||
public_key,
|
||||
secret_key,
|
||||
hops,
|
||||
});
|
||||
|
||||
let rsd = RouteSpecDetail {
|
||||
route_spec,
|
||||
secret_key,
|
||||
hops,
|
||||
hop_node_refs,
|
||||
transfer_stats_down_up: Default::default(),
|
||||
latency_stats: Default::default(),
|
||||
latency_stats_accounting: Default::default(),
|
||||
transfer_stats_accounting: Default::default(),
|
||||
published: false,
|
||||
timestamp: cur_ts,
|
||||
created_ts: cur_ts,
|
||||
last_checked_ts: None,
|
||||
directions,
|
||||
};
|
||||
|
||||
None
|
||||
self.content.details.insert(public_key, rsd);
|
||||
|
||||
// xxx insert into cache too
|
||||
|
||||
Some(public_key)
|
||||
}
|
||||
|
||||
pub fn release_route(&mut self, spec: Arc<RouteSpec>) {}
|
||||
@@ -311,15 +433,22 @@ impl RouteSpecStore {
|
||||
/// Mark route as published
|
||||
/// When first deserialized, routes must be re-published in order to ensure they remain
|
||||
/// in the RouteSpecStore.
|
||||
pub fn publish_route(&mut self, spec: Arc<RouteSpec>) {
|
||||
//compile private route here?
|
||||
pub fn mark_route_published(&mut self, spec: Arc<RouteSpec>) {
|
||||
self.detail_mut(spec).published = true;
|
||||
}
|
||||
|
||||
pub fn record_latency(
|
||||
&mut self,
|
||||
spec: Arc<RouteSpec>,
|
||||
latency: u64,
|
||||
) -> veilid_api::LatencyStats {
|
||||
/// Mark route as checked
|
||||
pub fn touch_route_checked(&mut self, spec: Arc<RouteSpec>, cur_ts: u64) {
|
||||
self.detail_mut(spec).last_checked_ts = cur_ts;
|
||||
}
|
||||
|
||||
pub fn record_latency(&mut self, spec: Arc<RouteSpec>, latency: u64) {
|
||||
let lsa = self.detail_mut(spec).latency_stats_accounting;
|
||||
self.detail_mut(spec).latency_stats = lsa.record_latency(latency);
|
||||
}
|
||||
|
||||
pub fn latency_stats(&self, spec: Arc<RouteSpec>) -> LatencyStats {
|
||||
self.detail_mut(spec).latency_stats.clone()
|
||||
}
|
||||
|
||||
pub fn add_down(&mut self, spec: Arc<RouteSpec>, bytes: u64) {
|
||||
|
Reference in New Issue
Block a user