more refactor

This commit is contained in:
John Smith 2023-02-15 18:18:08 -05:00
parent f11dc8aaac
commit 8f9b9b58d5
18 changed files with 257 additions and 141 deletions

View File

@ -134,7 +134,7 @@ struct RouteHopData @0x8ce231f9d1b7adf2 {
struct RouteHop @0xf8f672d75cce0c3b { struct RouteHop @0xf8f672d75cce0c3b {
node :union { node :union {
nodeId @0 :TypedKey; # node id only for established routes nodeId @0 :PublicKey; # node id key only for established routes (kind is the same as the pr or sr it is part of)
peerInfo @1 :PeerInfo; # full peer info for this hop to establish the route peerInfo @1 :PeerInfo; # full peer info for this hop to establish the route
} }
nextHop @2 :RouteHopData; # optional: If this the end of a private route, this field will not exist nextHop @2 :RouteHopData; # optional: If this the end of a private route, this field will not exist

View File

@ -36,9 +36,14 @@ pub fn best_crypto_kind() -> CryptoKind {
VALID_CRYPTO_KINDS[0] VALID_CRYPTO_KINDS[0]
} }
/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one // Version number of envelope format
pub type EnvelopeVersion = u8; pub type EnvelopeVersion = u8;
/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one
pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [0u8]; pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [0u8];
/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid
pub const MAX_ENVELOPE_VERSIONS: usize = 3;
/// Return the best envelope version we support
pub fn best_envelope_version() -> EnvelopeVersion { pub fn best_envelope_version() -> EnvelopeVersion {
VALID_ENVELOPE_VERSIONS[0] VALID_ENVELOPE_VERSIONS[0]
} }
@ -218,17 +223,19 @@ impl Crypto {
node_ids: &[TypedKey], node_ids: &[TypedKey],
data: &[u8], data: &[u8],
typed_signatures: &[TypedSignature], typed_signatures: &[TypedSignature],
) -> Result<(), VeilidAPIError> { ) -> Result<Vec<CryptoKind>, VeilidAPIError> {
let mut out = Vec::with_capacity(node_ids.len());
for sig in typed_signatures { for sig in typed_signatures {
for nid in node_ids { for nid in node_ids {
if nid.kind == sig.kind { if nid.kind == sig.kind {
if let Some(vcrypto) = self.get(sig.kind) { if let Some(vcrypto) = self.get(sig.kind) {
vcrypto.verify(&nid.key, data, &sig.signature)?; vcrypto.verify(&nid.key, data, &sig.signature)?;
out.push(nid.kind);
} }
} }
} }
} }
Ok(()) Ok(out)
} }
/// Signature set generation /// Signature set generation

View File

@ -27,6 +27,17 @@ pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering {
} }
} }
/// Intersection of crypto kind vectors
pub fn common_crypto_kinds(a: &[CryptoKind], b: &[CryptoKind]) -> Vec<CryptoKind> {
let mut out = Vec::new();
for ack in a {
if b.contains(ack) {
out.push(*ack);
}
}
out
}
#[derive( #[derive(
Clone, Clone,
Copy, Copy,
@ -42,7 +53,7 @@ pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering {
RkyvSerialize, RkyvSerialize,
RkyvDeserialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct KeyPair { pub struct KeyPair {
pub key: PublicKey, pub key: PublicKey,
pub secret: SecretKey, pub secret: SecretKey,
@ -67,7 +78,7 @@ impl KeyPair {
RkyvSerialize, RkyvSerialize,
RkyvDeserialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct TypedKey { pub struct TypedKey {
pub kind: CryptoKind, pub kind: CryptoKind,
pub key: PublicKey, pub key: PublicKey,
@ -126,7 +137,7 @@ impl FromStr for TypedKey {
RkyvSerialize, RkyvSerialize,
RkyvDeserialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct TypedKeySet { pub struct TypedKeySet {
items: Vec<TypedKey>, items: Vec<TypedKey>,
} }
@ -178,6 +189,11 @@ impl TypedKeySet {
self.items.remove(idx); self.items.remove(idx);
} }
} }
pub fn remove_all(&self, kinds: &[CryptoKind]) {
for k in kinds {
self.remove(*k);
}
}
pub fn best(&self) -> Option<TypedKey> { pub fn best(&self) -> Option<TypedKey> {
self.items.first().copied() self.items.first().copied()
} }
@ -255,7 +271,7 @@ impl FromStr for TypedKeySet {
RkyvSerialize, RkyvSerialize,
RkyvDeserialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct TypedKeyPair { pub struct TypedKeyPair {
pub kind: CryptoKind, pub kind: CryptoKind,
pub key: PublicKey, pub key: PublicKey,
@ -329,7 +345,7 @@ impl FromStr for TypedKeyPair {
RkyvSerialize, RkyvSerialize,
RkyvDeserialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct TypedSignature { pub struct TypedSignature {
pub kind: CryptoKind, pub kind: CryptoKind,
pub signature: Signature, pub signature: Signature,
@ -399,7 +415,7 @@ impl FromStr for TypedSignature {
RkyvSerialize, RkyvSerialize,
RkyvDeserialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct TypedKeySignature { pub struct TypedKeySignature {
pub kind: CryptoKind, pub kind: CryptoKind,
pub key: PublicKey, pub key: PublicKey,

View File

@ -893,7 +893,7 @@ impl NetworkManager {
// We expect the inbound noderef to be the same as the target noderef // We expect the inbound noderef to be the same as the target noderef
// if they aren't the same, we should error on this and figure out what then hell is up // if they aren't the same, we should error on this and figure out what then hell is up
if target_nr.node_id() != inbound_nr.node_id() { if !target_nr.same_entry(&inbound_nr) {
bail!("unexpected noderef mismatch on reverse connect"); bail!("unexpected noderef mismatch on reverse connect");
} }
@ -998,7 +998,7 @@ impl NetworkManager {
// We expect the inbound noderef to be the same as the target noderef // We expect the inbound noderef to be the same as the target noderef
// if they aren't the same, we should error on this and figure out what then hell is up // if they aren't the same, we should error on this and figure out what then hell is up
if target_nr.node_id() != inbound_nr.node_id() { if !target_nr.same_entry(&inbound_nr) {
bail!( bail!(
"unexpected noderef mismatch on hole punch {}, expected {}", "unexpected noderef mismatch on hole punch {}, expected {}",
inbound_nr, inbound_nr,

View File

@ -133,6 +133,15 @@ impl BucketEntryInner {
self.node_ids.best().unwrap() self.node_ids.best().unwrap()
} }
// Crypto kinds
pub fn crypto_kinds(&self) -> Vec<CryptoKind> {
self.node_ids.kinds()
}
pub fn common_crypto_kinds(&self, other: &[CryptoKind]) -> Vec<CryptoKind> {
common_crypto_kinds(&self.node_ids.kinds(), other)
}
// Less is faster // Less is faster
pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering { pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering {
// Lower latency to the front // Lower latency to the front

View File

@ -20,6 +20,14 @@ pub trait NodeRefBase: Sized {
fn common(&self) -> &NodeRefBaseCommon; fn common(&self) -> &NodeRefBaseCommon;
fn common_mut(&mut self) -> &mut NodeRefBaseCommon; fn common_mut(&mut self) -> &mut NodeRefBaseCommon;
// Comparators
fn same_entry<T: NodeRefBase>(&self, other: &T) -> bool {
Arc::ptr_eq(&self.common().entry, &other.common().entry)
}
fn same_bucket_entry(&self, entry: &Arc<BucketEntry>) -> bool {
Arc::ptr_eq(&self.common().entry, entry)
}
// Implementation-specific operators // Implementation-specific operators
fn operate<T, F>(&self, f: F) -> T fn operate<T, F>(&self, f: F) -> T
where where

View File

@ -20,18 +20,6 @@ pub enum RouteNode {
/// Route node with full contact method information to ensure the peer is reachable /// Route node with full contact method information to ensure the peer is reachable
PeerInfo(PeerInfo), PeerInfo(PeerInfo),
} }
impl fmt::Display for RouteNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
RouteNode::NodeId(x) => x.key.encode(),
RouteNode::PeerInfo(pi) => pi.node_id.key.encode(),
}
)
}
}
/// An unencrypted private/safety route hop /// An unencrypted private/safety route hop
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View File

@ -221,7 +221,7 @@ impl RouteSpecDetail {
#[archive_attr(repr(C, align(8)), derive(CheckBytes))] #[archive_attr(repr(C, align(8)), derive(CheckBytes))]
pub struct RouteSpecStoreContent { pub struct RouteSpecStoreContent {
/// All of the routes we have allocated so far /// All of the routes we have allocated so far
details: HashMap<PublicKey, RouteSpecDetail>, details: HashMap<TypedKey, RouteSpecDetail>,
} }
/// What remote private routes have seen /// What remote private routes have seen
@ -250,19 +250,19 @@ impl RemotePrivateRouteInfo {
#[derive(Debug)] #[derive(Debug)]
pub struct RouteSpecStoreCache { pub struct RouteSpecStoreCache {
/// How many times nodes have been used /// How many times nodes have been used
used_nodes: HashMap<PublicKey, usize>, used_nodes: HashMap<TypedKey, usize>,
/// How many times nodes have been used at the terminal point of a route /// How many times nodes have been used at the terminal point of a route
used_end_nodes: HashMap<PublicKey, usize>, used_end_nodes: HashMap<TypedKey, usize>,
/// Route spec hop cache, used to quickly disqualify routes /// Route spec hop cache, used to quickly disqualify routes
hop_cache: HashSet<Vec<u8>>, hop_cache: HashSet<Vec<u8>>,
/// Has a remote private route responded to a question and when /// Has a remote private route responded to a question and when
remote_private_route_cache: LruCache<PublicKey, RemotePrivateRouteInfo>, remote_private_route_cache: LruCache<TypedKey, RemotePrivateRouteInfo>,
/// Compiled route cache /// Compiled route cache
compiled_route_cache: LruCache<CompiledRouteCacheKey, SafetyRoute>, compiled_route_cache: LruCache<CompiledRouteCacheKey, SafetyRoute>,
/// List of dead allocated routes /// List of dead allocated routes
dead_routes: Vec<PublicKey>, dead_routes: Vec<TypedKey>,
/// List of dead remote routes /// List of dead remote routes
dead_remote_routes: Vec<PublicKey>, dead_remote_routes: Vec<TypedKey>,
} }
impl Default for RouteSpecStoreCache { impl Default for RouteSpecStoreCache {
@ -577,15 +577,15 @@ impl RouteSpecStore {
fn detail<'a>( fn detail<'a>(
inner: &'a RouteSpecStoreInner, inner: &'a RouteSpecStoreInner,
public_key: &PublicKey, route_spec_key: &TypedKey,
) -> Option<&'a RouteSpecDetail> { ) -> Option<&'a RouteSpecDetail> {
inner.content.details.get(public_key) inner.content.details.get(route_spec_key)
} }
fn detail_mut<'a>( fn detail_mut<'a>(
inner: &'a mut RouteSpecStoreInner, inner: &'a mut RouteSpecStoreInner,
public_key: &PublicKey, route_spec_key: &TypedKey,
) -> Option<&'a mut RouteSpecDetail> { ) -> Option<&'a mut RouteSpecDetail> {
inner.content.details.get_mut(public_key) inner.content.details.get_mut(route_spec_key)
} }
/// Purge the route spec store /// Purge the route spec store
@ -605,12 +605,13 @@ impl RouteSpecStore {
#[instrument(level = "trace", skip(self), ret, err)] #[instrument(level = "trace", skip(self), ret, err)]
pub fn allocate_route( pub fn allocate_route(
&self, &self,
crypto_kinds: &[CryptoKind],
stability: Stability, stability: Stability,
sequencing: Sequencing, sequencing: Sequencing,
hop_count: usize, hop_count: usize,
directions: DirectionSet, directions: DirectionSet,
avoid_node_ids: &[PublicKey], avoid_nodes: &[TypedKey],
) -> EyreResult<Option<PublicKey>> { ) -> EyreResult<Option<TypedKeySet>> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let routing_table = self.unlocked_inner.routing_table.clone(); let routing_table = self.unlocked_inner.routing_table.clone();
let rti = &mut *routing_table.inner.write(); let rti = &mut *routing_table.inner.write();
@ -618,11 +619,12 @@ impl RouteSpecStore {
self.allocate_route_inner( self.allocate_route_inner(
inner, inner,
rti, rti,
crypto_kinds,
stability, stability,
sequencing, sequencing,
hop_count, hop_count,
directions, directions,
avoid_node_ids, avoid_nodes,
) )
} }
@ -631,12 +633,13 @@ impl RouteSpecStore {
&self, &self,
inner: &mut RouteSpecStoreInner, inner: &mut RouteSpecStoreInner,
rti: &RoutingTableInner, rti: &RoutingTableInner,
crypto_kinds: &[CryptoKind],
stability: Stability, stability: Stability,
sequencing: Sequencing, sequencing: Sequencing,
hop_count: usize, hop_count: usize,
directions: DirectionSet, directions: DirectionSet,
avoid_node_ids: &[PublicKey], avoid_nodes: &[TypedKey],
) -> EyreResult<Option<PublicKey>> { ) -> EyreResult<Option<TypedKeySet>> {
use core::cmp::Ordering; use core::cmp::Ordering;
if hop_count < 1 { if hop_count < 1 {
@ -651,52 +654,60 @@ impl RouteSpecStore {
bail!("Can't allocate route until we have our own peer info"); bail!("Can't allocate route until we have our own peer info");
}; };
// Get relay node id if we have one // Get relay node if we have one
let opt_relay_id = rti let opt_own_relay_nr = rti.relay_node(RoutingDomain::PublicInternet).map(|nr| nr.locked(rti));
.relay_node(RoutingDomain::PublicInternet)
.map(|nr| nr.node_id());
// Get list of all nodes, and sort them for selection // Get list of all nodes, and sort them for selection
let cur_ts = get_aligned_timestamp(); let cur_ts = get_aligned_timestamp();
let filter = Box::new( let filter = Box::new(
move |rti: &RoutingTableInner, k: PublicKey, v: Option<Arc<BucketEntry>>| -> bool { move |rti: &RoutingTableInner, entry: Option<Arc<BucketEntry>>| -> bool {
// Exclude our own node from routes // Exclude our own node from routes
if v.is_none() { if entry.is_none() {
return false; return false;
} }
let v = v.unwrap(); let entry = entry.unwrap();
// Exclude our relay if we have one // Exclude our relay if we have one
if let Some(own_relay_id) = opt_relay_id { if let Some(own_relay_nr) = opt_own_relay_nr {
if k == own_relay_id { if own_relay_nr.same_bucket_entry(&entry) {
return false; return false;
} }
} }
// Exclude nodes we have specifically chosen to avoid
if avoid_node_ids.contains(&k) {
return false;
}
// Process node info exclusions // Process node info exclusions
let keep = v.with(rti, |_rti, e| { let keep = entry.with(rti, |_rti, e| {
// Exclude nodes that don't have our requested crypto kinds
let common_ck = e.common_crypto_kinds(crypto_kinds);
if common_ck.len() != crypto_kinds.len() {
return false;
}
// Exclude nodes we have specifically chosen to avoid
if e.node_ids().contains_any(avoid_nodes) {
return false;
}
// Exclude nodes on our local network // Exclude nodes on our local network
if e.node_info(RoutingDomain::LocalNetwork).is_some() { if e.node_info(RoutingDomain::LocalNetwork).is_some() {
return false; return false;
} }
// Exclude nodes that have no publicinternet signednodeinfo // Exclude nodes that have no publicinternet signednodeinfo
let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) else { let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) else {
return false; return false;
}; };
// Relay check // Relay check
if let Some(relay_id) = sni.relay_id() { let relay_ids = sni.relay_ids();
if relay_ids.len() != 0 {
// Exclude nodes whose relays we have chosen to avoid // Exclude nodes whose relays we have chosen to avoid
if avoid_node_ids.contains(&relay_id.key) { if relay_ids.contains_any(avoid_nodes) {
return false; return false;
} }
// Exclude nodes whose relay is our own relay if we have one // Exclude nodes whose relay is our own relay if we have one
if let Some(own_relay_id) = opt_relay_id { if let Some(own_relay_nr) = opt_own_relay_nr {
if own_relay_id == relay_id.key { if relay_ids.contains_any(&own_relay_nr.node_ids()) {
return false; return false;
} }
} }
@ -708,7 +719,7 @@ impl RouteSpecStore {
} }
// Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route // Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route
v.with(rti, move |_rti, e| { entry.with(rti, |_rti, e| {
let node_info_ok = let node_info_ok =
if let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) { if let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) {
sni.has_sequencing_matched_dial_info(sequencing) sni.has_sequencing_matched_dial_info(sequencing)
@ -728,9 +739,14 @@ impl RouteSpecStore {
) as RoutingTableEntryFilter; ) as RoutingTableEntryFilter;
let filters = VecDeque::from([filter]); let filters = VecDeque::from([filter]);
let compare = |rti: &RoutingTableInner, let compare = |rti: &RoutingTableInner,
v1: &(PublicKey, Option<Arc<BucketEntry>>), entry1: &Option<Arc<BucketEntry>>,
v2: &(PublicKey, Option<Arc<BucketEntry>>)| entry2: &Option<Arc<BucketEntry>>|
-> Ordering { -> Ordering {
// xxx also sort my most overlapping crypto kinds
// deprioritize nodes that we have already used as end points // deprioritize nodes that we have already used as end points
let e1_used_end = inner let e1_used_end = inner
.cache .cache
@ -1031,7 +1047,7 @@ impl RouteSpecStore {
} }
#[instrument(level = "trace", skip(self), ret, err)] #[instrument(level = "trace", skip(self), ret, err)]
async fn test_allocated_route(&self, key: &PublicKey) -> EyreResult<bool> { async fn test_allocated_route(&self, key: &TypedKey) -> EyreResult<bool> {
// Make loopback route to test with // Make loopback route to test with
let dest = { let dest = {
let private_route = self.assemble_private_route(key, None)?; let private_route = self.assemble_private_route(key, None)?;
@ -1074,7 +1090,7 @@ impl RouteSpecStore {
} }
#[instrument(level = "trace", skip(self), ret, err)] #[instrument(level = "trace", skip(self), ret, err)]
async fn test_remote_route(&self, key: &PublicKey) -> EyreResult<bool> { async fn test_remote_route(&self, key: &TypedKey) -> EyreResult<bool> {
// Make private route test // Make private route test
let dest = { let dest = {
// Get the route to test // Get the route to test
@ -1114,7 +1130,7 @@ impl RouteSpecStore {
/// Test an allocated route for continuity /// Test an allocated route for continuity
#[instrument(level = "trace", skip(self), ret, err)] #[instrument(level = "trace", skip(self), ret, err)]
pub async fn test_route(&self, key: &PublicKey) -> EyreResult<bool> { pub async fn test_route(&self, key: &[TypedKey]) -> EyreResult<bool> {
let is_remote = { let is_remote = {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let cur_ts = get_aligned_timestamp(); let cur_ts = get_aligned_timestamp();
@ -1574,8 +1590,8 @@ impl RouteSpecStore {
rti: &RoutingTableInner, rti: &RoutingTableInner,
safety_spec: &SafetySpec, safety_spec: &SafetySpec,
direction: DirectionSet, direction: DirectionSet,
avoid_node_ids: &[PublicKey], avoid_nodes: &[TypedKey],
) -> EyreResult<Option<PublicKey>> { ) -> EyreResult<Option<TypedKeySet>> {
// Ensure the total hop count isn't too long for our config // Ensure the total hop count isn't too long for our config
let max_route_hop_count = self.unlocked_inner.max_route_hop_count; let max_route_hop_count = self.unlocked_inner.max_route_hop_count;
if safety_spec.hop_count == 0 { if safety_spec.hop_count == 0 {
@ -1634,8 +1650,8 @@ impl RouteSpecStore {
pub fn get_private_route_for_safety_spec( pub fn get_private_route_for_safety_spec(
&self, &self,
safety_spec: &SafetySpec, safety_spec: &SafetySpec,
avoid_node_ids: &[PublicKey], avoid_nodes: &[TypedKey],
) -> EyreResult<Option<PublicKey>> { ) -> EyreResult<Option<TypedKey>> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let routing_table = self.unlocked_inner.routing_table.clone(); let routing_table = self.unlocked_inner.routing_table.clone();
let rti = &*routing_table.inner.read(); let rti = &*routing_table.inner.read();
@ -1645,7 +1661,7 @@ impl RouteSpecStore {
rti, rti,
safety_spec, safety_spec,
Direction::Inbound.into(), Direction::Inbound.into(),
avoid_node_ids, avoid_nodes,
)?) )?)
} }
@ -1742,7 +1758,7 @@ impl RouteSpecStore {
/// Import a remote private route for compilation /// Import a remote private route for compilation
#[instrument(level = "trace", skip(self, blob), ret, err)] #[instrument(level = "trace", skip(self, blob), ret, err)]
pub fn import_remote_private_route(&self, blob: Vec<u8>) -> EyreResult<PublicKey> { pub fn import_remote_private_route(&self, blob: Vec<u8>) -> EyreResult<TypedKey> {
// decode the pr blob // decode the pr blob
let private_route = RouteSpecStore::blob_to_private_route(blob)?; let private_route = RouteSpecStore::blob_to_private_route(blob)?;
@ -1767,7 +1783,7 @@ impl RouteSpecStore {
/// Release a remote private route that is no longer in use /// Release a remote private route that is no longer in use
#[instrument(level = "trace", skip(self), ret)] #[instrument(level = "trace", skip(self), ret)]
fn release_remote_private_route(&self, key: &PublicKey) -> bool { fn release_remote_private_route(&self, key: &TypedKey) -> bool {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
if inner.cache.remote_private_route_cache.remove(key).is_some() { if inner.cache.remote_private_route_cache.remove(key).is_some() {
// Mark it as dead for the update // Mark it as dead for the update
@ -1779,7 +1795,7 @@ impl RouteSpecStore {
} }
/// Retrieve an imported remote private route by its public key /// Retrieve an imported remote private route by its public key
pub fn get_remote_private_route(&self, key: &PublicKey) -> Option<PrivateRoute> { pub fn get_remote_private_route(&self, key: &TypedKey) -> Option<PrivateRoute> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let cur_ts = get_aligned_timestamp(); let cur_ts = get_aligned_timestamp();
Self::with_get_remote_private_route(inner, cur_ts, key, |r| { Self::with_get_remote_private_route(inner, cur_ts, key, |r| {
@ -1788,7 +1804,7 @@ impl RouteSpecStore {
} }
/// Retrieve an imported remote private route by its public key but don't 'touch' it /// Retrieve an imported remote private route by its public key but don't 'touch' it
pub fn peek_remote_private_route(&self, key: &PublicKey) -> Option<PrivateRoute> { pub fn peek_remote_private_route(&self, key: &TypedKey) -> Option<PrivateRoute> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let cur_ts = get_aligned_timestamp(); let cur_ts = get_aligned_timestamp();
Self::with_peek_remote_private_route(inner, cur_ts, key, |r| { Self::with_peek_remote_private_route(inner, cur_ts, key, |r| {
@ -1849,7 +1865,7 @@ impl RouteSpecStore {
fn with_get_remote_private_route<F, R>( fn with_get_remote_private_route<F, R>(
inner: &mut RouteSpecStoreInner, inner: &mut RouteSpecStoreInner,
cur_ts: Timestamp, cur_ts: Timestamp,
key: &PublicKey, remote_private_route: &TypedKey,
f: F, f: F,
) -> Option<R> ) -> Option<R>
where where
@ -1869,7 +1885,7 @@ impl RouteSpecStore {
fn with_peek_remote_private_route<F, R>( fn with_peek_remote_private_route<F, R>(
inner: &mut RouteSpecStoreInner, inner: &mut RouteSpecStoreInner,
cur_ts: Timestamp, cur_ts: Timestamp,
key: &PublicKey, remote_private_route: &TypedKey,
f: F, f: F,
) -> Option<R> ) -> Option<R>
where where
@ -1891,7 +1907,7 @@ impl RouteSpecStore {
/// Check to see if this remote (not ours) private route has seen our current node info yet /// Check to see if this remote (not ours) private route has seen our current node info yet
/// This happens when you communicate with a private route without a safety route /// This happens when you communicate with a private route without a safety route
pub fn has_remote_private_route_seen_our_node_info(&self, key: &PublicKey) -> bool { pub fn has_remote_private_route_seen_our_node_info(&self, remote_private_route: &TypedKey) -> bool {
let our_node_info_ts = { let our_node_info_ts = {
let rti = &*self.unlocked_inner.routing_table.inner.read(); let rti = &*self.unlocked_inner.routing_table.inner.read();
let Some(ts) = rti.get_own_node_info_ts(RoutingDomain::PublicInternet) else { let Some(ts) = rti.get_own_node_info_ts(RoutingDomain::PublicInternet) else {
@ -1903,7 +1919,7 @@ impl RouteSpecStore {
let opt_rpr_node_info_ts = { let opt_rpr_node_info_ts = {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let cur_ts = get_aligned_timestamp(); let cur_ts = get_aligned_timestamp();
Self::with_peek_remote_private_route(inner, cur_ts, key, |rpr| { Self::with_peek_remote_private_route(inner, cur_ts, remote_private_route, |rpr| {
rpr.last_seen_our_node_info_ts rpr.last_seen_our_node_info_ts
}) })
}; };

View File

@ -360,7 +360,7 @@ impl RoutingTableInner {
self.all_entries.remove_expired(); self.all_entries.remove_expired();
log_rtab!(debug log_rtab!(debug
"Routing table buckets purge complete. Routing table now has {} nodes", "Routing table buckets purge complete. Routing table now has {} nodes",
self.bucket_entry_count() self.bucket_entry_count()
); );
} }
@ -383,8 +383,8 @@ impl RoutingTableInner {
self.all_entries.remove_expired(); self.all_entries.remove_expired();
log_rtab!(debug log_rtab!(debug
"Routing table last_connections purge complete. Routing table now has {} nodes", "Routing table last_connections purge complete. Routing table now has {} nodes",
self.bucket_entry_count() self.bucket_entry_count()
); );
} }
@ -506,9 +506,9 @@ impl RoutingTableInner {
} }
pub fn get_all_nodes(&self, outer_self: RoutingTable, cur_ts: Timestamp) -> Vec<NodeRef> { pub fn get_all_nodes(&self, outer_self: RoutingTable, cur_ts: Timestamp) -> Vec<NodeRef> {
let mut node_refs = Vec::<NodeRef>::with_capacity(self.bucket_entry_count); let mut node_refs = Vec::<NodeRef>::with_capacity(self.bucket_entry_count());
self.with_entries(cur_ts, BucketEntryState::Unreliable, |_rti, k, v| { self.with_entries(cur_ts, BucketEntryState::Unreliable, |_rti, entry| {
node_refs.push(NodeRef::new(outer_self.clone(), k, v, None)); node_refs.push(NodeRef::new(outer_self.clone(), entry, None));
Option::<()>::None Option::<()>::None
}); });
node_refs node_refs
@ -525,7 +525,7 @@ impl RoutingTableInner {
) -> Option<NodeRef> ) -> Option<NodeRef>
where where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner), F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner),
{ {xxx continue here
// Ensure someone isn't trying register this node itself // Ensure someone isn't trying register this node itself
if self.unlocked_inner.matches_own_node_id(node_ids) { if self.unlocked_inner.matches_own_node_id(node_ids) {
log_rtab!(debug "can't register own node"); log_rtab!(debug "can't register own node");

View File

@ -25,7 +25,6 @@ impl RoutingTable {
// Get the PublicInternet relay if we are using one // Get the PublicInternet relay if we are using one
let opt_relay_nr = self.relay_node(RoutingDomain::PublicInternet); let opt_relay_nr = self.relay_node(RoutingDomain::PublicInternet);
let opt_relay_id = opt_relay_nr.map(|nr| nr.node_id());
// Get our publicinternet dial info // Get our publicinternet dial info
let dids = self.all_filtered_dial_info_details( let dids = self.all_filtered_dial_info_details(
@ -35,38 +34,42 @@ impl RoutingTable {
// For all nodes needing pings, figure out how many and over what protocols // For all nodes needing pings, figure out how many and over what protocols
for nr in node_refs { for nr in node_refs {
// If this is a relay, let's check for NAT keepalives // If this is our relay, let's check for NAT keepalives
let mut did_pings = false; let mut did_pings = false;
if Some(nr.node_id()) == opt_relay_id { if let Some(relay_nr) = opt_relay_nr {
// Relay nodes get pinged over all protocols we have inbound dialinfo for if nr.same_entry(&relay_nr) {
// This is so we can preserve the inbound NAT mappings at our router // Relay nodes get pinged over all protocols we have inbound dialinfo for
for did in &dids { // This is so we can preserve the inbound NAT mappings at our router
// Do we need to do this ping? for did in &dids {
// Check if we have already pinged over this low-level-protocol/address-type/port combo // Do we need to do this ping?
// We want to ensure we do the bare minimum required here // Check if we have already pinged over this low-level-protocol/address-type/port combo
let pt = did.dial_info.protocol_type(); // We want to ensure we do the bare minimum required here
let at = did.dial_info.address_type(); let pt = did.dial_info.protocol_type();
let needs_ping = if let Some((llpt, port)) = let at = did.dial_info.address_type();
mapped_port_info.protocol_to_port.get(&(pt, at)) let needs_ping = if let Some((llpt, port)) =
{ mapped_port_info.protocol_to_port.get(&(pt, at))
mapped_port_info {
.low_level_protocol_ports mapped_port_info
.remove(&(*llpt, at, *port)) .low_level_protocol_ports
} else { .remove(&(*llpt, at, *port))
false } else {
}; false
if needs_ping { };
let rpc = rpc.clone(); if needs_ping {
let dif = did.dial_info.make_filter(); let rpc = rpc.clone();
let nr_filtered = let dif = did.dial_info.make_filter();
nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif)); let nr_filtered =
log_net!("--> Keepalive ping to {:?}", nr_filtered); nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif));
unord.push( log_net!("--> Keepalive ping to {:?}", nr_filtered);
async move { rpc.rpc_call_status(Destination::direct(nr_filtered)).await } unord.push(
async move {
rpc.rpc_call_status(Destination::direct(nr_filtered)).await
}
.instrument(Span::current()) .instrument(Span::current())
.boxed(), .boxed(),
); );
did_pings = true; did_pings = true;
}
} }
} }
} }

View File

@ -79,6 +79,21 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
.map(|s| s.to_vec()) .map(|s| s.to_vec())
.unwrap_or_default(); .unwrap_or_default();
// Ensure envelope versions are not duplicated
// Unsorted is okay, some nodes may have a different envelope order preference
// But nothing should show up more than once
let mut eversions = envelope_support.clone();
eversions.dedup();
if eversions.len() != envelope_support.len() {
return Err(RPCError::protocol("duplicate envelope versions"));
}
if envelope_support.len() > MAX_ENVELOPE_VERSIONS {
return Err(RPCError::protocol("too many envelope versions"));
}
if envelope_support.len() == 0 {
return Err(RPCError::protocol("no envelope versions"));
}
let crypto_support: Vec<CryptoKind> = reader let crypto_support: Vec<CryptoKind> = reader
.reborrow() .reborrow()
.get_crypto_support() .get_crypto_support()
@ -87,6 +102,21 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
.map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect())
.unwrap_or_default(); .unwrap_or_default();
// Ensure crypto kinds are not duplicated
// Unsorted is okay, some nodes may have a different crypto order preference
// But nothing should show up more than once
let mut ckinds = crypto_support.clone();
ckinds.dedup();
if ckinds.len() != crypto_support.len() {
return Err(RPCError::protocol("duplicate crypto kinds"));
}
if crypto_support.len() > MAX_CRYPTO_KINDS {
return Err(RPCError::protocol("too many crypto kinds"));
}
if crypto_support.len() == 0 {
return Err(RPCError::protocol("no crypto kinds"));
}
let didl_reader = reader let didl_reader = reader
.reborrow() .reborrow()
.get_dial_info_detail_list() .get_dial_info_detail_list()

View File

@ -43,8 +43,10 @@ pub fn decode_peer_info(
for nid_reader in nids_reader.iter() { for nid_reader in nids_reader.iter() {
node_ids.add(decode_typed_key(&nid_reader)?); node_ids.add(decode_typed_key(&nid_reader)?);
} }
let signed_node_info = decode_signed_node_info(&sni_reader, crypto, &node_ids)?; let signed_node_info = decode_signed_node_info(&sni_reader, crypto, &mut node_ids)?;
if node_ids.len() == 0 {
return Err(RPCError::protocol("no verified node ids"));
}
Ok(PeerInfo { Ok(PeerInfo {
node_ids, node_ids,
signed_node_info, signed_node_info,

View File

@ -35,7 +35,7 @@ pub fn encode_signed_direct_node_info(
pub fn decode_signed_direct_node_info( pub fn decode_signed_direct_node_info(
reader: &veilid_capnp::signed_direct_node_info::Reader, reader: &veilid_capnp::signed_direct_node_info::Reader,
crypto: Crypto, crypto: Crypto,
node_ids: &[TypedKey], node_ids: &mut TypedKeySet,
) -> Result<SignedDirectNodeInfo, RPCError> { ) -> Result<SignedDirectNodeInfo, RPCError> {
let ni_reader = reader let ni_reader = reader
.reborrow() .reborrow()

View File

@ -21,7 +21,7 @@ pub fn encode_signed_node_info(
pub fn decode_signed_node_info( pub fn decode_signed_node_info(
reader: &veilid_capnp::signed_node_info::Reader, reader: &veilid_capnp::signed_node_info::Reader,
crypto: Crypto, crypto: Crypto,
node_ids: &[TypedKey], node_ids: &mut TypedKeySet,
) -> Result<SignedNodeInfo, RPCError> { ) -> Result<SignedNodeInfo, RPCError> {
match reader match reader
.which() .which()

View File

@ -55,7 +55,7 @@ pub fn encode_signed_relayed_node_info(
pub fn decode_signed_relayed_node_info( pub fn decode_signed_relayed_node_info(
reader: &veilid_capnp::signed_relayed_node_info::Reader, reader: &veilid_capnp::signed_relayed_node_info::Reader,
crypto: Crypto, crypto: Crypto,
node_ids: &[TypedKey], node_ids: &mut TypedKeySet,
) -> Result<SignedRelayedNodeInfo, RPCError> { ) -> Result<SignedRelayedNodeInfo, RPCError> {
let ni_reader = reader let ni_reader = reader
.reborrow() .reborrow()
@ -81,7 +81,20 @@ pub fn decode_signed_relayed_node_info(
.reborrow() .reborrow()
.get_relay_info() .get_relay_info()
.map_err(RPCError::protocol)?; .map_err(RPCError::protocol)?;
let relay_info = decode_signed_direct_node_info(&ri_reader, crypto, &relay_ids)?; let relay_info = decode_signed_direct_node_info(&ri_reader, crypto, &mut relay_ids)?;
// Ensure the relay info for the node has a superset of the crypto kinds of the node it is relaying
if common_crypto_kinds(
&node_info.crypto_support,
&relay_info.node_info.crypto_support,
)
.len()
!= node_info.crypto_support.len()
{
return Err(RPCError::protocol(
"relay should have superset of node crypto kinds",
));
}
let timestamp = reader.reborrow().get_timestamp().into(); let timestamp = reader.reborrow().get_timestamp().into();

View File

@ -15,7 +15,7 @@ pub enum Destination {
/// The relay to send to /// The relay to send to
relay: NodeRef, relay: NodeRef,
/// The final destination the relay should send to /// The final destination the relay should send to
target: TypedKey, target: NodeRef,
/// Require safety route or not /// Require safety route or not
safety_selection: SafetySelection, safety_selection: SafetySelection,
}, },
@ -36,7 +36,7 @@ impl Destination {
safety_selection: SafetySelection::Unsafe(sequencing), safety_selection: SafetySelection::Unsafe(sequencing),
} }
} }
pub fn relay(relay: NodeRef, target: TypedKey) -> Self { pub fn relay(relay: NodeRef, target: NodeRef) -> Self {
let sequencing = relay.sequencing(); let sequencing = relay.sequencing();
Self::Relay { Self::Relay {
relay, relay,
@ -124,7 +124,7 @@ impl fmt::Display for Destination {
"" ""
}; };
write!(f, "{}@{}{}", target.encode(), relay, sr) write!(f, "{}@{}{}", target, relay, sr)
} }
Destination::PrivateRoute { Destination::PrivateRoute {
private_route, private_route,
@ -163,7 +163,7 @@ impl RPCProcessor {
SafetySelection::Safe(safety_spec) => { SafetySelection::Safe(safety_spec) => {
// Sent directly but with a safety route, respond to private route // Sent directly but with a safety route, respond to private route
let Some(pr_key) = rss let Some(pr_key) = rss
.get_private_route_for_safety_spec(safety_spec, &[target.node_id()]) .get_private_route_for_safety_spec(safety_spec, &target.node_ids())
.map_err(RPCError::internal)? else { .map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time")); return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
}; };
@ -187,11 +187,13 @@ impl RPCProcessor {
} }
SafetySelection::Safe(safety_spec) => { SafetySelection::Safe(safety_spec) => {
// Sent via a relay but with a safety route, respond to private route // Sent via a relay but with a safety route, respond to private route
let mut avoid_nodes = relay.node_ids();
avoid_nodes.add_all(&target.node_ids());
let Some(pr_key) = rss let Some(pr_key) = rss
.get_private_route_for_safety_spec(safety_spec, &[relay.node_id(), *target]) .get_private_route_for_safety_spec(safety_spec, &avoid_nodes)
.map_err(RPCError::internal)? else { .map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time")); return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
}; };
// Get the assembled route for response // Get the assembled route for response
let private_route = rss let private_route = rss
@ -209,6 +211,8 @@ impl RPCProcessor {
return Err(RPCError::internal("destination private route must have first hop")); return Err(RPCError::internal("destination private route must have first hop"));
}; };
let crypto_kind = private_route.public_key.kind;
match safety_selection { match safety_selection {
SafetySelection::Unsafe(_) => { SafetySelection::Unsafe(_) => {
// Sent to a private route with no safety route, use a stub safety route for the response // Sent to a private route with no safety route, use a stub safety route for the response
@ -221,7 +225,7 @@ impl RPCProcessor {
if !routing_table.has_valid_own_node_info(RoutingDomain::PublicInternet) { if !routing_table.has_valid_own_node_info(RoutingDomain::PublicInternet) {
return Ok(NetworkResult::no_connection_other("Own node info must be valid to use private route")); return Ok(NetworkResult::no_connection_other("Own node info must be valid to use private route"));
} }
RouteNode::NodeId(NodeId::new(routing_table.node_id())) RouteNode::NodeId(routing_table.node_id(crypto_kind))
} }
false => { false => {
let Some(own_peer_info) = let Some(own_peer_info) =
@ -233,7 +237,7 @@ impl RPCProcessor {
}; };
Ok(NetworkResult::value(RespondTo::PrivateRoute( Ok(NetworkResult::value(RespondTo::PrivateRoute(
PrivateRoute::new_stub(routing_table.node_id(), route_node), PrivateRoute::new_stub(routing_table.node_id(crypto_kind), route_node),
))) )))
} }
SafetySelection::Safe(safety_spec) => { SafetySelection::Safe(safety_spec) => {

View File

@ -167,17 +167,22 @@ impl VeilidAPI {
// Private route allocation // Private route allocation
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
pub async fn new_private_route(&self) -> Result<(TypedKey, Vec<u8>), VeilidAPIError> { pub async fn new_private_route(&self) -> Result<(TypedKeySet, Vec<u8>), VeilidAPIError> {
self.new_custom_private_route(Stability::default(), Sequencing::default()) self.new_custom_private_route(
.await &VALID_CRYPTO_KINDS,
Stability::default(),
Sequencing::default(),
)
.await
} }
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
pub async fn new_custom_private_route( pub async fn new_custom_private_route(
&self, &self,
crypto_kinds: &[CryptoKind],
stability: Stability, stability: Stability,
sequencing: Sequencing, sequencing: Sequencing,
) -> Result<(TypedKey, Vec<u8>), VeilidAPIError> { ) -> Result<(TypedKeySet, Vec<u8>), VeilidAPIError> {
let default_route_hop_count: usize = { let default_route_hop_count: usize = {
let config = self.config()?; let config = self.config()?;
let c = config.get(); let c = config.get();
@ -187,6 +192,7 @@ impl VeilidAPI {
let rss = self.routing_table()?.route_spec_store(); let rss = self.routing_table()?.route_spec_store();
let r = rss let r = rss
.allocate_route( .allocate_route(
&crypto_kinds,
stability, stability,
sequencing, sequencing,
default_route_hop_count, default_route_hop_count,
@ -194,7 +200,7 @@ impl VeilidAPI {
&[], &[],
) )
.map_err(VeilidAPIError::internal)?; .map_err(VeilidAPIError::internal)?;
let Some(pr_pubkey) = r else { let Some(pr_keys) = r else {
apibail_generic!("unable to allocate route"); apibail_generic!("unable to allocate route");
}; };
if !rss if !rss

View File

@ -35,7 +35,7 @@ pub type ValueSchema = FourCC;
RkyvSerialize, RkyvSerialize,
RkyvDeserialize, RkyvDeserialize,
)] )]
#[archive_attr(repr(C), derive(CheckBytes, PartialOrd, Ord, PartialEq, Eq))] #[archive_attr(repr(C), derive(CheckBytes, PartialOrd, Ord, PartialEq, Eq, Hash))]
pub struct FourCC(pub [u8; 4]); pub struct FourCC(pub [u8; 4]);
impl From<[u8; 4]> for FourCC { impl From<[u8; 4]> for FourCC {
@ -513,7 +513,7 @@ impl SafetySelection {
#[archive_attr(repr(C), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes))]
pub struct SafetySpec { pub struct SafetySpec {
/// preferred safety route if it still exists /// preferred safety route if it still exists
pub preferred_route: Option<PublicKey>, pub preferred_route: Option<TypedKey>,
/// must be greater than 0 /// must be greater than 0
pub hop_count: usize, pub hop_count: usize,
/// prefer reliability over speed /// prefer reliability over speed
@ -1853,7 +1853,7 @@ pub struct SignedDirectNodeInfo {
impl SignedDirectNodeInfo { impl SignedDirectNodeInfo {
pub fn new( pub fn new(
crypto: Crypto, crypto: Crypto,
node_ids: &[TypedKey], node_ids: &mut TypedKeySet,
node_info: NodeInfo, node_info: NodeInfo,
timestamp: Timestamp, timestamp: Timestamp,
typed_signatures: Vec<TypedSignature>, typed_signatures: Vec<TypedSignature>,
@ -1861,7 +1861,13 @@ impl SignedDirectNodeInfo {
let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?;
// Verify the signatures that we can // Verify the signatures that we can
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; let valid_crypto_kinds =
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?;
node_ids.remove_all(&valid_crypto_kinds);
if node_ids.len() == 0 {
apibail_generic!("no valid node ids in direct node info");
}
Ok(Self { Ok(Self {
node_info, node_info,
timestamp, timestamp,
@ -1933,7 +1939,7 @@ pub struct SignedRelayedNodeInfo {
impl SignedRelayedNodeInfo { impl SignedRelayedNodeInfo {
pub fn new( pub fn new(
crypto: Crypto, crypto: Crypto,
node_ids: &[TypedKey], node_ids: &mut TypedKeySet,
node_info: NodeInfo, node_info: NodeInfo,
relay_ids: TypedKeySet, relay_ids: TypedKeySet,
relay_info: SignedDirectNodeInfo, relay_info: SignedDirectNodeInfo,
@ -1942,7 +1948,14 @@ impl SignedRelayedNodeInfo {
) -> Result<Self, VeilidAPIError> { ) -> Result<Self, VeilidAPIError> {
let node_info_bytes = let node_info_bytes =
Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?; Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?;
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; let valid_crypto_kinds =
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?;
node_ids.remove_all(&valid_crypto_kinds);
if node_ids.len() == 0 {
apibail_generic!("no valid node ids in relayed node info");
}
Ok(Self { Ok(Self {
node_info, node_info,
relay_ids, relay_ids,
@ -2113,6 +2126,7 @@ pub struct PeerInfo {
impl PeerInfo { impl PeerInfo {
pub fn new(node_ids: TypedKeySet, signed_node_info: SignedNodeInfo) -> Self { pub fn new(node_ids: TypedKeySet, signed_node_info: SignedNodeInfo) -> Self {
assert!(node_ids.len() > 0 && node_ids.len() <= MAX_CRYPTO_KINDS);
Self { Self {
node_ids, node_ids,
signed_node_info, signed_node_info,