refactor api to clean up internals

This commit is contained in:
John Smith
2023-04-19 10:47:09 -04:00
parent 00b0edf687
commit b4a071170d
74 changed files with 3638 additions and 3027 deletions

View File

@@ -10,6 +10,7 @@ mod routing_domains;
mod routing_table_inner;
mod stats_accounting;
mod tasks;
mod types;
use crate::*;
@@ -17,9 +18,10 @@ use crate::crypto::*;
use crate::network_manager::*;
use crate::rpc_processor::*;
use bucket::*;
use hashlink::LruCache;
pub use bucket_entry::*;
pub use debug::*;
use hashlink::LruCache;
pub use node_ref::*;
pub use node_ref_filter::*;
pub use privacy::*;
@@ -28,6 +30,7 @@ pub use routing_domain_editor::*;
pub use routing_domains::*;
pub use routing_table_inner::*;
pub use stats_accounting::*;
pub use types::*;
//////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,43 @@
use super::*;
// Keep member order appropriate for sorting < preference
#[derive(
Debug,
Clone,
PartialEq,
PartialOrd,
Ord,
Eq,
Hash,
Serialize,
Deserialize,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct DialInfoDetail {
pub class: DialInfoClass,
pub dial_info: DialInfo,
}
impl MatchesDialInfoFilter for DialInfoDetail {
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
self.dial_info.matches_filter(filter)
}
}
impl DialInfoDetail {
pub fn ordered_sequencing_sort(a: &DialInfoDetail, b: &DialInfoDetail) -> core::cmp::Ordering {
if a.class < b.class {
return core::cmp::Ordering::Less;
}
if a.class > b.class {
return core::cmp::Ordering::Greater;
}
DialInfo::ordered_sequencing_sort(&a.dial_info, &b.dial_info)
}
pub const NO_SORT: std::option::Option<
for<'r, 's> fn(&'r DialInfoDetail, &'s DialInfoDetail) -> std::cmp::Ordering,
> = None::<fn(&DialInfoDetail, &DialInfoDetail) -> core::cmp::Ordering>;
}

View File

@@ -0,0 +1,22 @@
use super::*;
#[allow(clippy::derive_hash_xor_eq)]
#[derive(
Debug,
PartialOrd,
Ord,
Hash,
EnumSetType,
Serialize,
Deserialize,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[enumset(repr = "u8")]
#[archive_attr(repr(u8), derive(CheckBytes))]
pub enum Direction {
Inbound,
Outbound,
}
pub type DirectionSet = EnumSet<Direction>;

View File

@@ -0,0 +1,25 @@
mod dial_info_detail;
mod direction;
mod node_info;
mod node_status;
mod peer_info;
mod routing_domain;
mod signed_direct_node_info;
mod signed_node_info;
mod signed_relayed_node_info;
use super::*;
pub use dial_info_detail::*;
pub use direction::*;
pub use node_info::*;
pub use node_status::*;
pub use peer_info::*;
pub use routing_domain::*;
pub use signed_direct_node_info::*;
pub use signed_node_info::*;
pub use signed_relayed_node_info::*;
use enumset::*;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
use serde::*;

View File

@@ -0,0 +1,125 @@
use super::*;
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct NodeInfo {
pub network_class: NetworkClass,
#[with(RkyvEnumSet)]
pub outbound_protocols: ProtocolTypeSet,
#[with(RkyvEnumSet)]
pub address_types: AddressTypeSet,
pub envelope_support: Vec<u8>,
pub crypto_support: Vec<CryptoKind>,
pub dial_info_detail_list: Vec<DialInfoDetail>,
}
impl NodeInfo {
pub fn first_filtered_dial_info_detail<S, F>(
&self,
sort: Option<S>,
filter: F,
) -> Option<DialInfoDetail>
where
S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering,
F: Fn(&DialInfoDetail) -> bool,
{
if let Some(sort) = sort {
let mut dids = self.dial_info_detail_list.clone();
dids.sort_by(sort);
for did in dids {
if filter(&did) {
return Some(did);
}
}
} else {
for did in &self.dial_info_detail_list {
if filter(did) {
return Some(did.clone());
}
}
};
None
}
pub fn all_filtered_dial_info_details<S, F>(
&self,
sort: Option<S>,
filter: F,
) -> Vec<DialInfoDetail>
where
S: Fn(&DialInfoDetail, &DialInfoDetail) -> std::cmp::Ordering,
F: Fn(&DialInfoDetail) -> bool,
{
let mut dial_info_detail_list = Vec::new();
if let Some(sort) = sort {
let mut dids = self.dial_info_detail_list.clone();
dids.sort_by(sort);
for did in dids {
if filter(&did) {
dial_info_detail_list.push(did);
}
}
} else {
for did in &self.dial_info_detail_list {
if filter(did) {
dial_info_detail_list.push(did.clone());
}
}
};
dial_info_detail_list
}
/// Does this node has some dial info
pub fn has_dial_info(&self) -> bool {
!self.dial_info_detail_list.is_empty()
}
/// Is some relay required either for signal or inbound relay or outbound relay?
pub fn requires_relay(&self) -> bool {
match self.network_class {
NetworkClass::InboundCapable => {
for did in &self.dial_info_detail_list {
if did.class.requires_relay() {
return true;
}
}
}
NetworkClass::OutboundOnly => {
return true;
}
NetworkClass::WebApp => {
return true;
}
NetworkClass::Invalid => {}
}
false
}
/// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself.
pub fn can_signal(&self) -> bool {
// Must be inbound capable
if !matches!(self.network_class, NetworkClass::InboundCapable) {
return false;
}
// Do any of our dial info require signalling? if so, we can't offer signalling
for did in &self.dial_info_detail_list {
if did.class.requires_signal() {
return false;
}
}
true
}
/// Can this node relay be an inbound relay?
pub fn can_inbound_relay(&self) -> bool {
// For now this is the same
self.can_signal()
}
/// Is this node capable of validating dial info
pub fn can_validate_dial_info(&self) -> bool {
// For now this is the same
self.can_signal()
}
}

View File

@@ -0,0 +1,66 @@
use super::*;
/// RoutingDomain-specific status for each node
/// is returned by the StatusA call
/// PublicInternet RoutingDomain Status
#[derive(
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct PublicInternetNodeStatus {
pub will_route: bool,
pub will_tunnel: bool,
pub will_signal: bool,
pub will_relay: bool,
pub will_validate_dial_info: bool,
}
#[derive(
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct LocalNetworkNodeStatus {
pub will_relay: bool,
pub will_validate_dial_info: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(u8), derive(CheckBytes))]
pub enum NodeStatus {
PublicInternet(PublicInternetNodeStatus),
LocalNetwork(LocalNetworkNodeStatus),
}
impl NodeStatus {
pub fn will_route(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_route,
NodeStatus::LocalNetwork(_) => false,
}
}
pub fn will_tunnel(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_tunnel,
NodeStatus::LocalNetwork(_) => false,
}
}
pub fn will_signal(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_signal,
NodeStatus::LocalNetwork(_) => false,
}
}
pub fn will_relay(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_relay,
NodeStatus::LocalNetwork(ln) => ln.will_relay,
}
}
pub fn will_validate_dial_info(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_validate_dial_info,
NodeStatus::LocalNetwork(ln) => ln.will_validate_dial_info,
}
}
}

View File

@@ -0,0 +1,18 @@
use super::*;
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct PeerInfo {
pub node_ids: TypedKeySet,
pub signed_node_info: SignedNodeInfo,
}
impl PeerInfo {
pub fn new(node_ids: TypedKeySet, signed_node_info: SignedNodeInfo) -> Self {
assert!(node_ids.len() > 0 && node_ids.len() <= MAX_CRYPTO_KINDS);
Self {
node_ids,
signed_node_info,
}
}
}

View File

@@ -0,0 +1,32 @@
use super::*;
// Routing domain here is listed in order of preference, keep in order
#[allow(clippy::derive_hash_xor_eq)]
#[derive(
Debug,
Ord,
PartialOrd,
Hash,
EnumSetType,
Serialize,
Deserialize,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[enumset(repr = "u8")]
#[archive_attr(repr(u8), derive(CheckBytes))]
pub enum RoutingDomain {
LocalNetwork = 0,
PublicInternet = 1,
}
impl RoutingDomain {
pub const fn count() -> usize {
2
}
pub const fn all() -> [RoutingDomain; RoutingDomain::count()] {
// Routing domain here is listed in order of preference, keep in order
[RoutingDomain::LocalNetwork, RoutingDomain::PublicInternet]
}
}
pub type RoutingDomainSet = EnumSet<RoutingDomain>;

View File

@@ -0,0 +1,86 @@
use super::*;
/// Signed NodeInfo that can be passed around amongst peers and verifiable
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct SignedDirectNodeInfo {
pub node_info: NodeInfo,
pub timestamp: Timestamp,
pub signatures: Vec<TypedSignature>,
}
impl SignedDirectNodeInfo {
/// Returns a new SignedDirectNodeInfo that has its signatures validated.
/// On success, this will modify the node_ids set to only include node_ids whose signatures validate.
/// All signatures are stored however, as this can be passed to other nodes that may be able to validate those signatures.
pub fn new(
crypto: Crypto,
node_ids: &mut TypedKeySet,
node_info: NodeInfo,
timestamp: Timestamp,
typed_signatures: Vec<TypedSignature>,
) -> Result<Self, VeilidAPIError> {
let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?;
// Verify the signatures that we can
let validated_node_ids =
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?;
*node_ids = validated_node_ids;
if node_ids.len() == 0 {
apibail_generic!("no valid node ids in direct node info");
}
Ok(Self {
node_info,
timestamp,
signatures: typed_signatures,
})
}
pub fn make_signatures(
crypto: Crypto,
typed_key_pairs: Vec<TypedKeyPair>,
node_info: NodeInfo,
) -> Result<Self, VeilidAPIError> {
let timestamp = get_aligned_timestamp();
let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?;
let typed_signatures =
crypto.generate_signatures(&node_info_bytes, &typed_key_pairs, |kp, s| {
TypedSignature::new(kp.kind, s)
})?;
Ok(Self {
node_info,
timestamp,
signatures: typed_signatures,
})
}
fn make_signature_bytes(
node_info: &NodeInfo,
timestamp: Timestamp,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut node_info_bytes = Vec::new();
// Add nodeinfo to signature
let mut ni_msg = ::capnp::message::Builder::new_default();
let mut ni_builder = ni_msg.init_root::<veilid_capnp::node_info::Builder>();
encode_node_info(node_info, &mut ni_builder).map_err(VeilidAPIError::internal)?;
node_info_bytes.append(&mut builder_to_vec(ni_msg).map_err(VeilidAPIError::internal)?);
// Add timestamp to signature
node_info_bytes.append(&mut timestamp.as_u64().to_le_bytes().to_vec());
Ok(node_info_bytes)
}
pub fn with_no_signature(node_info: NodeInfo) -> Self {
Self {
node_info,
timestamp: get_aligned_timestamp(),
signatures: Vec::new(),
}
}
pub fn has_any_signature(&self) -> bool {
!self.signatures.is_empty()
}
}

View File

@@ -0,0 +1,89 @@
use super::*;
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(u8), derive(CheckBytes))]
pub enum SignedNodeInfo {
Direct(SignedDirectNodeInfo),
Relayed(SignedRelayedNodeInfo),
}
impl SignedNodeInfo {
pub fn has_any_signature(&self) -> bool {
match self {
SignedNodeInfo::Direct(d) => d.has_any_signature(),
SignedNodeInfo::Relayed(r) => r.has_any_signature(),
}
}
pub fn timestamp(&self) -> Timestamp {
match self {
SignedNodeInfo::Direct(d) => d.timestamp,
SignedNodeInfo::Relayed(r) => r.timestamp,
}
}
pub fn node_info(&self) -> &NodeInfo {
match self {
SignedNodeInfo::Direct(d) => &d.node_info,
SignedNodeInfo::Relayed(r) => &r.node_info,
}
}
pub fn relay_ids(&self) -> TypedKeySet {
match self {
SignedNodeInfo::Direct(_) => TypedKeySet::new(),
SignedNodeInfo::Relayed(r) => r.relay_ids.clone(),
}
}
pub fn relay_info(&self) -> Option<&NodeInfo> {
match self {
SignedNodeInfo::Direct(_) => None,
SignedNodeInfo::Relayed(r) => Some(&r.relay_info.node_info),
}
}
pub fn relay_peer_info(&self) -> Option<PeerInfo> {
match self {
SignedNodeInfo::Direct(_) => None,
SignedNodeInfo::Relayed(r) => Some(PeerInfo::new(
r.relay_ids.clone(),
SignedNodeInfo::Direct(r.relay_info.clone()),
)),
}
}
pub fn has_any_dial_info(&self) -> bool {
self.node_info().has_dial_info()
|| self
.relay_info()
.map(|relay_ni| relay_ni.has_dial_info())
.unwrap_or_default()
}
pub fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool {
// Check our dial info
for did in &self.node_info().dial_info_detail_list {
match sequencing {
Sequencing::NoPreference | Sequencing::PreferOrdered => return true,
Sequencing::EnsureOrdered => {
if did.dial_info.protocol_type().is_connection_oriented() {
return true;
}
}
}
}
// Check our relay if we have one
return self
.relay_info()
.map(|relay_ni| {
for did in &relay_ni.dial_info_detail_list {
match sequencing {
Sequencing::NoPreference | Sequencing::PreferOrdered => return true,
Sequencing::EnsureOrdered => {
if did.dial_info.protocol_type().is_connection_oriented() {
return true;
}
}
}
}
false
})
.unwrap_or_default();
}
}

View File

@@ -0,0 +1,106 @@
use super::*;
/// Signed NodeInfo with a relay that can be passed around amongst peers and verifiable
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct SignedRelayedNodeInfo {
pub node_info: NodeInfo,
pub relay_ids: TypedKeySet,
pub relay_info: SignedDirectNodeInfo,
pub timestamp: Timestamp,
pub signatures: Vec<TypedSignature>,
}
impl SignedRelayedNodeInfo {
/// Returns a new SignedRelayedNodeInfo that has its signatures validated.
/// On success, this will modify the node_ids set to only include node_ids whose signatures validate.
/// All signatures are stored however, as this can be passed to other nodes that may be able to validate those signatures.
pub fn new(
crypto: Crypto,
node_ids: &mut TypedKeySet,
node_info: NodeInfo,
relay_ids: TypedKeySet,
relay_info: SignedDirectNodeInfo,
timestamp: Timestamp,
typed_signatures: Vec<TypedSignature>,
) -> Result<Self, VeilidAPIError> {
let node_info_bytes =
Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?;
let validated_node_ids =
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?;
*node_ids = validated_node_ids;
if node_ids.len() == 0 {
apibail_generic!("no valid node ids in relayed node info");
}
Ok(Self {
node_info,
relay_ids,
relay_info,
timestamp,
signatures: typed_signatures,
})
}
pub fn make_signatures(
crypto: Crypto,
typed_key_pairs: Vec<TypedKeyPair>,
node_info: NodeInfo,
relay_ids: TypedKeySet,
relay_info: SignedDirectNodeInfo,
) -> Result<Self, VeilidAPIError> {
let timestamp = get_aligned_timestamp();
let node_info_bytes =
Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?;
let typed_signatures =
crypto.generate_signatures(&node_info_bytes, &typed_key_pairs, |kp, s| {
TypedSignature::new(kp.kind, s)
})?;
Ok(Self {
node_info,
relay_ids,
relay_info,
timestamp,
signatures: typed_signatures,
})
}
fn make_signature_bytes(
node_info: &NodeInfo,
relay_ids: &[TypedKey],
relay_info: &SignedDirectNodeInfo,
timestamp: Timestamp,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut sig_bytes = Vec::new();
// Add nodeinfo to signature
let mut ni_msg = ::capnp::message::Builder::new_default();
let mut ni_builder = ni_msg.init_root::<veilid_capnp::node_info::Builder>();
encode_node_info(node_info, &mut ni_builder).map_err(VeilidAPIError::internal)?;
sig_bytes.append(&mut builder_to_vec(ni_msg).map_err(VeilidAPIError::internal)?);
// Add relay ids to signature
for relay_id in relay_ids {
let mut rid_msg = ::capnp::message::Builder::new_default();
let mut rid_builder = rid_msg.init_root::<veilid_capnp::typed_key::Builder>();
encode_typed_key(relay_id, &mut rid_builder);
sig_bytes.append(&mut builder_to_vec(rid_msg).map_err(VeilidAPIError::internal)?);
}
// Add relay info to signature
let mut ri_msg = ::capnp::message::Builder::new_default();
let mut ri_builder = ri_msg.init_root::<veilid_capnp::signed_direct_node_info::Builder>();
encode_signed_direct_node_info(relay_info, &mut ri_builder)
.map_err(VeilidAPIError::internal)?;
sig_bytes.append(&mut builder_to_vec(ri_msg).map_err(VeilidAPIError::internal)?);
// Add timestamp to signature
sig_bytes.append(&mut timestamp.as_u64().to_le_bytes().to_vec());
Ok(sig_bytes)
}
pub fn has_any_signature(&self) -> bool {
!self.signatures.is_empty()
}
}