mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-01-13 00:09:47 -05:00
refactor api to clean up internals
This commit is contained in:
parent
00b0edf687
commit
b4a071170d
@ -55,11 +55,7 @@ impl TableViewItem<PeerTableColumn> for PeerTableData {
|
|||||||
.first()
|
.first()
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| "???".to_owned()),
|
.unwrap_or_else(|| "???".to_owned()),
|
||||||
PeerTableColumn::Address => format!(
|
PeerTableColumn::Address => self.peer_address.clone(),
|
||||||
"{:?}:{}",
|
|
||||||
self.peer_address.protocol_type(),
|
|
||||||
self.peer_address.to_socket_addr()
|
|
||||||
),
|
|
||||||
PeerTableColumn::LatencyAvg => format!(
|
PeerTableColumn::LatencyAvg => format!(
|
||||||
"{}",
|
"{}",
|
||||||
self.peer_stats
|
self.peer_stats
|
||||||
|
@ -311,17 +311,16 @@ struct OperationAppMessage @0x9baf542d81b411f5 {
|
|||||||
message @0 :Data; # opaque message to application
|
message @0 :Data; # opaque message to application
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SubkeyRange {
|
struct SubkeyRange @0xf592dac0a4d0171c {
|
||||||
start @0 :Subkey; # the start of a subkey range
|
start @0 :Subkey; # the start of a subkey range
|
||||||
end @1 :Subkey; # the end of a subkey range
|
end @1 :Subkey; # the end of a subkey range
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ValueData @0xb4b7416f169f2a3d {
|
struct SignedValueData @0xb4b7416f169f2a3d {
|
||||||
seq @0 :ValueSeqNum; # sequence number of value
|
seq @0 :ValueSeqNum; # sequence number of value
|
||||||
data @1 :Data; # value or subvalue contents
|
data @1 :Data; # value or subvalue contents
|
||||||
owner @2 :PublicKey; # the public key of the owner
|
writer @2 :PublicKey; # the public key of the writer
|
||||||
writer @3 :PublicKey; # the public key of the writer
|
signature @3 :Signature; # signature of data at this subkey, using the writer key (which may be the same as the owner key)
|
||||||
signature @4 :Signature; # signature of data at this subkey, using the writer key (which may be the same as the owner key)
|
|
||||||
# signature covers:
|
# signature covers:
|
||||||
# * ownerKey
|
# * ownerKey
|
||||||
# * subkey
|
# * subkey
|
||||||
@ -329,23 +328,32 @@ struct ValueData @0xb4b7416f169f2a3d {
|
|||||||
# * data
|
# * data
|
||||||
# signature does not need to cover schema because schema is validated upon every set
|
# signature does not need to cover schema because schema is validated upon every set
|
||||||
# so the data either fits, or it doesn't.
|
# so the data either fits, or it doesn't.
|
||||||
schema @5 :Data; # (optional) the schema in use
|
|
||||||
# If not set and seqnum == 0, uses the default schema.
|
|
||||||
# If not set and If seqnum != 0, the schema must have been set prior and no other schema may be used, but this field may be eliminated to save space
|
|
||||||
# Changing this after key creation is not supported as it would change the dht key
|
|
||||||
# Schema data is signed by ownerKey and is verified both by set and get operations
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ValueDetail @0xe88607fa8982d67f {
|
||||||
|
signedValueData @0 :SignedValueData; # One value data with signature
|
||||||
|
descriptor @1 :SignedValueDescriptor; # (optional) must provide if seq is 0, and may be present from valueget if wantSchema is specified
|
||||||
|
# If not set and If seqnum != 0, the schema must have been set prior and no other schema may be used, but this field may be eliminated to save space
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SignedValueDescriptor @0xe7911cd3f9e1b0e7 {
|
||||||
|
owner @0 :PublicKey; # the public key of the owner
|
||||||
|
schemaData @1 :Data; # the schema data
|
||||||
|
# Changing this after key creation is not supported as it would change the dht key
|
||||||
|
signature @2 :Signature; # Schema data is signed by ownerKey and is verified both by set and get operations
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct OperationGetValueQ @0xf88a5b6da5eda5d0 {
|
struct OperationGetValueQ @0xf88a5b6da5eda5d0 {
|
||||||
key @0 :TypedKey; # the location of the value
|
key @0 :TypedKey; # DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ]
|
||||||
subkey @1 :Subkey; # the index of the subkey
|
subkey @1 :Subkey; # the index of the subkey
|
||||||
wantSchema @2 :Bool; # whether or not to include the schema for the key
|
wantDescriptor @2 :Bool; # whether or not to include the descriptor for the key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct OperationGetValueA @0xd896bb46f2e0249f {
|
struct OperationGetValueA @0xd896bb46f2e0249f {
|
||||||
union {
|
union {
|
||||||
data @0 :ValueData; # the value if successful
|
value @0 :ValueDetail; # the value if successful
|
||||||
peers @1 :List(PeerInfo); # returned 'closer peer' information if not successful
|
peers @1 :List(PeerInfo); # returned 'closer peer' information if not successful
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,13 +361,13 @@ struct OperationGetValueA @0xd896bb46f2e0249f {
|
|||||||
struct OperationSetValueQ @0xbac06191ff8bdbc5 {
|
struct OperationSetValueQ @0xbac06191ff8bdbc5 {
|
||||||
key @0 :TypedKey; # DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ]
|
key @0 :TypedKey; # DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ]
|
||||||
subkey @1 :Subkey; # the index of the subkey
|
subkey @1 :Subkey; # the index of the subkey
|
||||||
value @2 :ValueData; # value or subvalue contents (older or equal seq number gets dropped)
|
value @2 :ValueDetail; # value or subvalue contents (older or equal seq number gets dropped)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OperationSetValueA @0x9378d0732dc95be2 {
|
struct OperationSetValueA @0x9378d0732dc95be2 {
|
||||||
union {
|
union {
|
||||||
schemaError @0 :Void; # Either the schema is not available at the node, or the data does not match the schema that is there
|
schemaError @0 :Void; # Either the schema is not available at the node, or the data does not match the schema that is there
|
||||||
data @1 :ValueData; # the new value if successful, may be a different value than what was set if the seq number was lower or equal
|
value @1 :ValueDetail; # the new value if successful, may be a different value than what was set if the seq number was lower or equal
|
||||||
peers @2 :List(PeerInfo); # returned 'closer peer' information if this node is refusing to store the key
|
peers @2 :List(PeerInfo); # returned 'closer peer' information if this node is refusing to store the key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,7 +389,7 @@ struct OperationValueChanged @0xd1c59ebdd8cc1bf6 {
|
|||||||
key @0 :TypedKey; # key for value that changed
|
key @0 :TypedKey; # key for value that changed
|
||||||
subkeys @1 :List(SubkeyRange); # subkey range that changed (up to 512 ranges at a time)
|
subkeys @1 :List(SubkeyRange); # subkey range that changed (up to 512 ranges at a time)
|
||||||
count @2 :UInt32; # remaining changes left (0 means watch has expired)
|
count @2 :UInt32; # remaining changes left (0 means watch has expired)
|
||||||
value @3 :ValueData; # first value that changed (the rest can be gotten with getvalue)
|
value @3 :ValueDetail; # first value that changed (the rest can be gotten with getvalue)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OperationSupplyBlockQ @0xadbf4c542d749971 {
|
struct OperationSupplyBlockQ @0xadbf4c542d749971 {
|
||||||
|
@ -4,7 +4,6 @@ mod dh_cache;
|
|||||||
mod envelope;
|
mod envelope;
|
||||||
mod receipt;
|
mod receipt;
|
||||||
mod types;
|
mod types;
|
||||||
mod value;
|
|
||||||
|
|
||||||
pub mod crypto_system;
|
pub mod crypto_system;
|
||||||
#[cfg(feature = "enable-crypto-none")]
|
#[cfg(feature = "enable-crypto-none")]
|
||||||
@ -20,7 +19,6 @@ pub use dh_cache::*;
|
|||||||
pub use envelope::*;
|
pub use envelope::*;
|
||||||
pub use receipt::*;
|
pub use receipt::*;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
pub use value::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "enable-crypto-none")]
|
#[cfg(feature = "enable-crypto-none")]
|
||||||
pub use none::*;
|
pub use none::*;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub async fn get_outbound_relay_peer() -> Option<crate::veilid_api::PeerInfo> {
|
pub async fn get_outbound_relay_peer() -> Option<crate::routing_table::PeerInfo> {
|
||||||
panic!("Native Veilid should never require an outbound relay");
|
panic!("Native Veilid should never require an outbound relay");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use crate::*;
|
|||||||
|
|
||||||
//use js_sys::*;
|
//use js_sys::*;
|
||||||
|
|
||||||
pub async fn get_outbound_relay_peer() -> Option<crate::veilid_api::PeerInfo> {
|
pub async fn get_outbound_relay_peer() -> Option<crate::routing_table::PeerInfo> {
|
||||||
// unimplemented!
|
// unimplemented!
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ mod connection_manager;
|
|||||||
mod connection_table;
|
mod connection_table;
|
||||||
mod network_connection;
|
mod network_connection;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
|
mod types;
|
||||||
|
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ pub mod tests;
|
|||||||
|
|
||||||
pub use connection_manager::*;
|
pub use connection_manager::*;
|
||||||
pub use network_connection::*;
|
pub use network_connection::*;
|
||||||
|
pub use types::*;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
use connection_handle::*;
|
use connection_handle::*;
|
||||||
@ -1552,7 +1554,7 @@ impl NetworkManager {
|
|||||||
let peer_stats = nr.peer_stats();
|
let peer_stats = nr.peer_stats();
|
||||||
let peer = PeerTableData {
|
let peer = PeerTableData {
|
||||||
node_ids: nr.node_ids().iter().map(|x| x.to_string()).collect(),
|
node_ids: nr.node_ids().iter().map(|x| x.to_string()).collect(),
|
||||||
peer_address: v.last_connection.remote(),
|
peer_address: v.last_connection.remote().to_string(),
|
||||||
peer_stats,
|
peer_stats,
|
||||||
};
|
};
|
||||||
out.push(peer);
|
out.push(peer);
|
||||||
|
@ -645,7 +645,7 @@ impl Network {
|
|||||||
log_net!(debug "enable address {:?} as ipv4", addr);
|
log_net!(debug "enable address {:?} as ipv4", addr);
|
||||||
inner.enable_ipv4 = true;
|
inner.enable_ipv4 = true;
|
||||||
} else if addr.is_ipv6() {
|
} else if addr.is_ipv6() {
|
||||||
let address = crate::Address::from_ip_addr(addr);
|
let address = Address::from_ip_addr(addr);
|
||||||
if address.is_global() {
|
if address.is_global() {
|
||||||
log_net!(debug "enable address {:?} as ipv6 global", address);
|
log_net!(debug "enable address {:?} as ipv6 global", address);
|
||||||
inner.enable_ipv6_global = true;
|
inner.enable_ipv6_global = true;
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
pub mod test_connection_table;
|
pub mod test_connection_table;
|
||||||
|
pub mod test_signed_node_info;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
167
veilid-core/src/network_manager/tests/test_signed_node_info.rs
Normal file
167
veilid-core/src/network_manager/tests/test_signed_node_info.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::tests::common::test_veilid_config::*;
|
||||||
|
|
||||||
|
pub async fn test_signed_node_info() {
|
||||||
|
info!("--- test_signed_node_info ---");
|
||||||
|
|
||||||
|
let (update_callback, config_callback) = setup_veilid_core();
|
||||||
|
let api = api_startup(update_callback, config_callback)
|
||||||
|
.await
|
||||||
|
.expect("startup failed");
|
||||||
|
|
||||||
|
let crypto = api.crypto().unwrap();
|
||||||
|
for ck in VALID_CRYPTO_KINDS {
|
||||||
|
let vcrypto = crypto.get(ck).unwrap();
|
||||||
|
|
||||||
|
// Test direct
|
||||||
|
let node_info = NodeInfo {
|
||||||
|
network_class: NetworkClass::InboundCapable,
|
||||||
|
outbound_protocols: ProtocolTypeSet::all(),
|
||||||
|
address_types: AddressTypeSet::all(),
|
||||||
|
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
|
||||||
|
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
|
||||||
|
dial_info_detail_list: vec![DialInfoDetail {
|
||||||
|
class: DialInfoClass::Mapped,
|
||||||
|
dial_info: DialInfo::udp(SocketAddress::default()),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test correct validation
|
||||||
|
let keypair = vcrypto.generate_keypair();
|
||||||
|
let sni = SignedDirectNodeInfo::make_signatures(
|
||||||
|
crypto.clone(),
|
||||||
|
vec![TypedKeyPair::new(ck, keypair)],
|
||||||
|
node_info.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut tks: TypedKeySet = TypedKey::new(ck, keypair.key).into();
|
||||||
|
let oldtkslen = tks.len();
|
||||||
|
let _ = SignedDirectNodeInfo::new(
|
||||||
|
crypto.clone(),
|
||||||
|
&mut tks,
|
||||||
|
node_info.clone(),
|
||||||
|
sni.timestamp,
|
||||||
|
sni.signatures.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(tks.len(), oldtkslen);
|
||||||
|
assert_eq!(tks.len(), sni.signatures.len());
|
||||||
|
|
||||||
|
// Test incorrect validation
|
||||||
|
let keypair1 = vcrypto.generate_keypair();
|
||||||
|
let mut tks1: TypedKeySet = TypedKey::new(ck, keypair1.key).into();
|
||||||
|
let oldtks1len = tks1.len();
|
||||||
|
let _ = SignedDirectNodeInfo::new(
|
||||||
|
crypto.clone(),
|
||||||
|
&mut tks1,
|
||||||
|
node_info.clone(),
|
||||||
|
sni.timestamp,
|
||||||
|
sni.signatures.clone(),
|
||||||
|
)
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(tks1.len(), oldtks1len);
|
||||||
|
assert_eq!(tks1.len(), sni.signatures.len());
|
||||||
|
|
||||||
|
// Test unsupported cryptosystem validation
|
||||||
|
let fake_crypto_kind: CryptoKind = FourCC::from([0, 1, 2, 3]);
|
||||||
|
let mut tksfake: TypedKeySet = TypedKey::new(fake_crypto_kind, PublicKey::default()).into();
|
||||||
|
let mut sigsfake = sni.signatures.clone();
|
||||||
|
sigsfake.push(TypedSignature::new(fake_crypto_kind, Signature::default()));
|
||||||
|
tksfake.add(TypedKey::new(ck, keypair.key));
|
||||||
|
let sdnifake = SignedDirectNodeInfo::new(
|
||||||
|
crypto.clone(),
|
||||||
|
&mut tksfake,
|
||||||
|
node_info.clone(),
|
||||||
|
sni.timestamp,
|
||||||
|
sigsfake.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(tksfake.len(), 1);
|
||||||
|
assert_eq!(sdnifake.signatures.len(), sigsfake.len());
|
||||||
|
|
||||||
|
// Test relayed
|
||||||
|
let node_info2 = NodeInfo {
|
||||||
|
network_class: NetworkClass::OutboundOnly,
|
||||||
|
outbound_protocols: ProtocolTypeSet::all(),
|
||||||
|
address_types: AddressTypeSet::all(),
|
||||||
|
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
|
||||||
|
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
|
||||||
|
dial_info_detail_list: vec![DialInfoDetail {
|
||||||
|
class: DialInfoClass::Blocked,
|
||||||
|
dial_info: DialInfo::udp(SocketAddress::default()),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test correct validation
|
||||||
|
let keypair2 = vcrypto.generate_keypair();
|
||||||
|
let mut tks2: TypedKeySet = TypedKey::new(ck, keypair2.key).into();
|
||||||
|
let oldtks2len = tks2.len();
|
||||||
|
|
||||||
|
let sni2 = SignedRelayedNodeInfo::make_signatures(
|
||||||
|
crypto.clone(),
|
||||||
|
vec![TypedKeyPair::new(ck, keypair2)],
|
||||||
|
node_info2.clone(),
|
||||||
|
tks.clone(),
|
||||||
|
sni.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let _ = SignedRelayedNodeInfo::new(
|
||||||
|
crypto.clone(),
|
||||||
|
&mut tks2,
|
||||||
|
node_info2.clone(),
|
||||||
|
tks.clone(),
|
||||||
|
sni.clone(),
|
||||||
|
sni2.timestamp,
|
||||||
|
sni2.signatures.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(tks2.len(), oldtks2len);
|
||||||
|
assert_eq!(tks2.len(), sni2.signatures.len());
|
||||||
|
|
||||||
|
// Test incorrect validation
|
||||||
|
let keypair3 = vcrypto.generate_keypair();
|
||||||
|
let mut tks3: TypedKeySet = TypedKey::new(ck, keypair3.key).into();
|
||||||
|
let oldtks3len = tks3.len();
|
||||||
|
|
||||||
|
let _ = SignedRelayedNodeInfo::new(
|
||||||
|
crypto.clone(),
|
||||||
|
&mut tks3,
|
||||||
|
node_info2.clone(),
|
||||||
|
tks.clone(),
|
||||||
|
sni.clone(),
|
||||||
|
sni2.timestamp,
|
||||||
|
sni2.signatures.clone(),
|
||||||
|
)
|
||||||
|
.unwrap_err();
|
||||||
|
|
||||||
|
assert_eq!(tks3.len(), oldtks3len);
|
||||||
|
assert_eq!(tks3.len(), sni2.signatures.len());
|
||||||
|
|
||||||
|
// Test unsupported cryptosystem validation
|
||||||
|
let fake_crypto_kind: CryptoKind = FourCC::from([0, 1, 2, 3]);
|
||||||
|
let mut tksfake3: TypedKeySet =
|
||||||
|
TypedKey::new(fake_crypto_kind, PublicKey::default()).into();
|
||||||
|
let mut sigsfake3 = sni2.signatures.clone();
|
||||||
|
sigsfake3.push(TypedSignature::new(fake_crypto_kind, Signature::default()));
|
||||||
|
tksfake3.add(TypedKey::new(ck, keypair2.key));
|
||||||
|
let srnifake = SignedRelayedNodeInfo::new(
|
||||||
|
crypto.clone(),
|
||||||
|
&mut tksfake3,
|
||||||
|
node_info2.clone(),
|
||||||
|
tks.clone(),
|
||||||
|
sni.clone(),
|
||||||
|
sni2.timestamp,
|
||||||
|
sigsfake3.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(tksfake3.len(), 1);
|
||||||
|
assert_eq!(srnifake.signatures.len(), sigsfake3.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
api.shutdown().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_all() {
|
||||||
|
test_signed_node_info().await;
|
||||||
|
}
|
130
veilid-core/src/network_manager/types/address.rs
Normal file
130
veilid-core/src/network_manager/types/address.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum Address {
|
||||||
|
IPV4(Ipv4Addr),
|
||||||
|
IPV6(Ipv6Addr),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Address {
|
||||||
|
fn default() -> Self {
|
||||||
|
Address::IPV4(Ipv4Addr::new(0, 0, 0, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
pub fn from_socket_addr(sa: SocketAddr) -> Address {
|
||||||
|
match sa {
|
||||||
|
SocketAddr::V4(v4) => Address::IPV4(*v4.ip()),
|
||||||
|
SocketAddr::V6(v6) => Address::IPV6(*v6.ip()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_ip_addr(addr: IpAddr) -> Address {
|
||||||
|
match addr {
|
||||||
|
IpAddr::V4(v4) => Address::IPV4(v4),
|
||||||
|
IpAddr::V6(v6) => Address::IPV6(v6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn address_type(&self) -> AddressType {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(_) => AddressType::IPV4,
|
||||||
|
Address::IPV6(_) => AddressType::IPV6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn address_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => v4.to_string(),
|
||||||
|
Address::IPV6(v6) => v6.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn address_string_with_port(&self, port: u16) -> String {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => format!("{}:{}", v4, port),
|
||||||
|
Address::IPV6(v6) => format!("[{}]:{}", v6, port),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_unspecified(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => ipv4addr_is_unspecified(v4),
|
||||||
|
Address::IPV6(v6) => ipv6addr_is_unspecified(v6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_global(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => ipv4addr_is_global(v4) && !ipv4addr_is_multicast(v4),
|
||||||
|
Address::IPV6(v6) => ipv6addr_is_unicast_global(v6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_local(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => {
|
||||||
|
ipv4addr_is_private(v4)
|
||||||
|
|| ipv4addr_is_link_local(v4)
|
||||||
|
|| ipv4addr_is_ietf_protocol_assignment(v4)
|
||||||
|
}
|
||||||
|
Address::IPV6(v6) => {
|
||||||
|
ipv6addr_is_unicast_site_local(v6)
|
||||||
|
|| ipv6addr_is_unicast_link_local(v6)
|
||||||
|
|| ipv6addr_is_unique_local(v6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_ip_addr(&self) -> IpAddr {
|
||||||
|
match self {
|
||||||
|
Self::IPV4(a) => IpAddr::V4(*a),
|
||||||
|
Self::IPV6(a) => IpAddr::V6(*a),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_socket_addr(&self, port: u16) -> SocketAddr {
|
||||||
|
SocketAddr::new(self.to_ip_addr(), port)
|
||||||
|
}
|
||||||
|
pub fn to_canonical(&self) -> Address {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => Address::IPV4(*v4),
|
||||||
|
Address::IPV6(v6) => match v6.to_ipv4() {
|
||||||
|
Some(v4) => Address::IPV4(v4),
|
||||||
|
None => Address::IPV6(*v6),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Address {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => write!(f, "{}", v4),
|
||||||
|
Address::IPV6(v6) => write!(f, "{}", v6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Address {
|
||||||
|
type Err = VeilidAPIError;
|
||||||
|
fn from_str(host: &str) -> Result<Address, VeilidAPIError> {
|
||||||
|
if let Ok(addr) = Ipv4Addr::from_str(host) {
|
||||||
|
Ok(Address::IPV4(addr))
|
||||||
|
} else if let Ok(addr) = Ipv6Addr::from_str(host) {
|
||||||
|
Ok(Address::IPV6(addr))
|
||||||
|
} else {
|
||||||
|
Err(VeilidAPIError::parse_error(
|
||||||
|
"Address::from_str failed",
|
||||||
|
host,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
veilid-core/src/network_manager/types/address_type.rs
Normal file
22
veilid-core/src/network_manager/types/address_type.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
EnumSetType,
|
||||||
|
)]
|
||||||
|
#[enumset(repr = "u8")]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum AddressType {
|
||||||
|
IPV4,
|
||||||
|
IPV6,
|
||||||
|
}
|
||||||
|
pub type AddressTypeSet = EnumSet<AddressType>;
|
@ -0,0 +1,80 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Represents the 5-tuple of an established connection
|
||||||
|
/// Not used to specify connections to create, that is reserved for DialInfo
|
||||||
|
///
|
||||||
|
/// ConnectionDescriptors should never be from unspecified local addresses for connection oriented protocols
|
||||||
|
/// If the medium does not allow local addresses, None should have been used or 'new_no_local'
|
||||||
|
/// If we are specifying only a port, then the socket's 'local_address()' should have been used, since an
|
||||||
|
/// established connection is always from a real address to another real address.
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct ConnectionDescriptor {
|
||||||
|
remote: PeerAddress,
|
||||||
|
local: Option<SocketAddress>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionDescriptor {
|
||||||
|
pub fn new(remote: PeerAddress, local: SocketAddress) -> Self {
|
||||||
|
assert!(
|
||||||
|
!remote.protocol_type().is_connection_oriented() || !local.address().is_unspecified()
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
remote,
|
||||||
|
local: Some(local),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new_no_local(remote: PeerAddress) -> Self {
|
||||||
|
Self {
|
||||||
|
remote,
|
||||||
|
local: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn remote(&self) -> PeerAddress {
|
||||||
|
self.remote
|
||||||
|
}
|
||||||
|
pub fn remote_address(&self) -> &SocketAddress {
|
||||||
|
self.remote.socket_address()
|
||||||
|
}
|
||||||
|
pub fn local(&self) -> Option<SocketAddress> {
|
||||||
|
self.local
|
||||||
|
}
|
||||||
|
pub fn protocol_type(&self) -> ProtocolType {
|
||||||
|
self.remote.protocol_type()
|
||||||
|
}
|
||||||
|
pub fn address_type(&self) -> AddressType {
|
||||||
|
self.remote.address_type()
|
||||||
|
}
|
||||||
|
pub fn make_dial_info_filter(&self) -> DialInfoFilter {
|
||||||
|
DialInfoFilter::all()
|
||||||
|
.with_protocol_type(self.protocol_type())
|
||||||
|
.with_address_type(self.address_type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MatchesDialInfoFilter for ConnectionDescriptor {
|
||||||
|
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
||||||
|
if !filter.protocol_type_set.contains(self.protocol_type()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !filter.address_type_set.contains(self.address_type()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
522
veilid-core/src/network_manager/types/dial_info/mod.rs
Normal file
522
veilid-core/src/network_manager/types/dial_info/mod.rs
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
mod tcp;
|
||||||
|
mod udp;
|
||||||
|
mod ws;
|
||||||
|
mod wss;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use tcp::*;
|
||||||
|
pub use udp::*;
|
||||||
|
pub use ws::*;
|
||||||
|
pub use wss::*;
|
||||||
|
|
||||||
|
// Keep member order appropriate for sorting < preference
|
||||||
|
// Must match ProtocolType order
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
|
pub enum DialInfo {
|
||||||
|
UDP(DialInfoUDP),
|
||||||
|
TCP(DialInfoTCP),
|
||||||
|
WS(DialInfoWS),
|
||||||
|
WSS(DialInfoWSS),
|
||||||
|
}
|
||||||
|
impl Default for DialInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
DialInfo::UDP(DialInfoUDP::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DialInfo {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
match self {
|
||||||
|
DialInfo::UDP(di) => write!(f, "udp|{}", di.socket_address),
|
||||||
|
DialInfo::TCP(di) => write!(f, "tcp|{}", di.socket_address),
|
||||||
|
DialInfo::WS(di) => {
|
||||||
|
let url = format!("ws://{}", di.request);
|
||||||
|
let split_url = SplitUrl::from_str(&url).unwrap();
|
||||||
|
match split_url.host {
|
||||||
|
SplitUrlHost::Hostname(_) => {
|
||||||
|
write!(f, "ws|{}|{}", di.socket_address.to_ip_addr(), di.request)
|
||||||
|
}
|
||||||
|
SplitUrlHost::IpAddr(a) => {
|
||||||
|
if di.socket_address.to_ip_addr() == a {
|
||||||
|
write!(f, "ws|{}", di.request)
|
||||||
|
} else {
|
||||||
|
panic!("resolved address does not match url: {}", di.request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DialInfo::WSS(di) => {
|
||||||
|
let url = format!("wss://{}", di.request);
|
||||||
|
let split_url = SplitUrl::from_str(&url).unwrap();
|
||||||
|
match split_url.host {
|
||||||
|
SplitUrlHost::Hostname(_) => {
|
||||||
|
write!(f, "wss|{}|{}", di.socket_address.to_ip_addr(), di.request)
|
||||||
|
}
|
||||||
|
SplitUrlHost::IpAddr(_) => {
|
||||||
|
panic!(
|
||||||
|
"secure websockets can not use ip address in request: {}",
|
||||||
|
di.request
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DialInfo {
|
||||||
|
type Err = VeilidAPIError;
|
||||||
|
fn from_str(s: &str) -> Result<DialInfo, VeilidAPIError> {
|
||||||
|
let (proto, rest) = s.split_once('|').ok_or_else(|| {
|
||||||
|
VeilidAPIError::parse_error("DialInfo::from_str missing protocol '|' separator", s)
|
||||||
|
})?;
|
||||||
|
match proto {
|
||||||
|
"udp" => {
|
||||||
|
let socket_address = SocketAddress::from_str(rest)?;
|
||||||
|
Ok(DialInfo::udp(socket_address))
|
||||||
|
}
|
||||||
|
"tcp" => {
|
||||||
|
let socket_address = SocketAddress::from_str(rest)?;
|
||||||
|
Ok(DialInfo::tcp(socket_address))
|
||||||
|
}
|
||||||
|
"ws" => {
|
||||||
|
let url = format!("ws://{}", rest);
|
||||||
|
let split_url = SplitUrl::from_str(&url).map_err(|e| {
|
||||||
|
VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url)
|
||||||
|
})?;
|
||||||
|
if split_url.scheme != "ws" || !url.starts_with("ws://") {
|
||||||
|
apibail_parse_error!("incorrect scheme for WS dialinfo", url);
|
||||||
|
}
|
||||||
|
let url_port = split_url.port.unwrap_or(80u16);
|
||||||
|
|
||||||
|
match rest.split_once('|') {
|
||||||
|
Some((sa, rest)) => {
|
||||||
|
let address = Address::from_str(sa)?;
|
||||||
|
|
||||||
|
DialInfo::try_ws(
|
||||||
|
SocketAddress::new(address, url_port),
|
||||||
|
format!("ws://{}", rest),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let address = Address::from_str(&split_url.host.to_string())?;
|
||||||
|
DialInfo::try_ws(
|
||||||
|
SocketAddress::new(address, url_port),
|
||||||
|
format!("ws://{}", rest),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"wss" => {
|
||||||
|
let url = format!("wss://{}", rest);
|
||||||
|
let split_url = SplitUrl::from_str(&url).map_err(|e| {
|
||||||
|
VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url)
|
||||||
|
})?;
|
||||||
|
if split_url.scheme != "wss" || !url.starts_with("wss://") {
|
||||||
|
apibail_parse_error!("incorrect scheme for WSS dialinfo", url);
|
||||||
|
}
|
||||||
|
let url_port = split_url.port.unwrap_or(443u16);
|
||||||
|
|
||||||
|
let (a, rest) = rest.split_once('|').ok_or_else(|| {
|
||||||
|
VeilidAPIError::parse_error(
|
||||||
|
"DialInfo::from_str missing socket address '|' separator",
|
||||||
|
s,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let address = Address::from_str(a)?;
|
||||||
|
DialInfo::try_wss(
|
||||||
|
SocketAddress::new(address, url_port),
|
||||||
|
format!("wss://{}", rest),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Err(VeilidAPIError::parse_error(
|
||||||
|
"DialInfo::from_str has invalid scheme",
|
||||||
|
s,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DialInfo {
|
||||||
|
pub fn udp_from_socketaddr(socket_addr: SocketAddr) -> Self {
|
||||||
|
Self::UDP(DialInfoUDP {
|
||||||
|
socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn tcp_from_socketaddr(socket_addr: SocketAddr) -> Self {
|
||||||
|
Self::TCP(DialInfoTCP {
|
||||||
|
socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn udp(socket_address: SocketAddress) -> Self {
|
||||||
|
Self::UDP(DialInfoUDP {
|
||||||
|
socket_address: socket_address.to_canonical(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn tcp(socket_address: SocketAddress) -> Self {
|
||||||
|
Self::TCP(DialInfoTCP {
|
||||||
|
socket_address: socket_address.to_canonical(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn try_ws(socket_address: SocketAddress, url: String) -> Result<Self, VeilidAPIError> {
|
||||||
|
let split_url = SplitUrl::from_str(&url).map_err(|e| {
|
||||||
|
VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url)
|
||||||
|
})?;
|
||||||
|
if split_url.scheme != "ws" || !url.starts_with("ws://") {
|
||||||
|
apibail_parse_error!("incorrect scheme for WS dialinfo", url);
|
||||||
|
}
|
||||||
|
let url_port = split_url.port.unwrap_or(80u16);
|
||||||
|
if url_port != socket_address.port() {
|
||||||
|
apibail_parse_error!("socket address port doesn't match url port", url);
|
||||||
|
}
|
||||||
|
if let SplitUrlHost::IpAddr(a) = split_url.host {
|
||||||
|
if socket_address.to_ip_addr() != a {
|
||||||
|
apibail_parse_error!(
|
||||||
|
format!("request address does not match socket address: {}", a),
|
||||||
|
socket_address
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self::WS(DialInfoWS {
|
||||||
|
socket_address: socket_address.to_canonical(),
|
||||||
|
request: url[5..].to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
pub fn try_wss(socket_address: SocketAddress, url: String) -> Result<Self, VeilidAPIError> {
|
||||||
|
let split_url = SplitUrl::from_str(&url).map_err(|e| {
|
||||||
|
VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url)
|
||||||
|
})?;
|
||||||
|
if split_url.scheme != "wss" || !url.starts_with("wss://") {
|
||||||
|
apibail_parse_error!("incorrect scheme for WSS dialinfo", url);
|
||||||
|
}
|
||||||
|
let url_port = split_url.port.unwrap_or(443u16);
|
||||||
|
if url_port != socket_address.port() {
|
||||||
|
apibail_parse_error!("socket address port doesn't match url port", url);
|
||||||
|
}
|
||||||
|
if !matches!(split_url.host, SplitUrlHost::Hostname(_)) {
|
||||||
|
apibail_parse_error!(
|
||||||
|
"WSS url can not use address format, only hostname format",
|
||||||
|
url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(Self::WSS(DialInfoWSS {
|
||||||
|
socket_address: socket_address.to_canonical(),
|
||||||
|
request: url[6..].to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
pub fn protocol_type(&self) -> ProtocolType {
|
||||||
|
match self {
|
||||||
|
Self::UDP(_) => ProtocolType::UDP,
|
||||||
|
Self::TCP(_) => ProtocolType::TCP,
|
||||||
|
Self::WS(_) => ProtocolType::WS,
|
||||||
|
Self::WSS(_) => ProtocolType::WSS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn address_type(&self) -> AddressType {
|
||||||
|
self.socket_address().address_type()
|
||||||
|
}
|
||||||
|
pub fn address(&self) -> Address {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address.address(),
|
||||||
|
Self::TCP(di) => di.socket_address.address(),
|
||||||
|
Self::WS(di) => di.socket_address.address(),
|
||||||
|
Self::WSS(di) => di.socket_address.address(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_address(&mut self, address: Address) {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address.set_address(address),
|
||||||
|
Self::TCP(di) => di.socket_address.set_address(address),
|
||||||
|
Self::WS(di) => di.socket_address.set_address(address),
|
||||||
|
Self::WSS(di) => di.socket_address.set_address(address),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn socket_address(&self) -> SocketAddress {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address,
|
||||||
|
Self::TCP(di) => di.socket_address,
|
||||||
|
Self::WS(di) => di.socket_address,
|
||||||
|
Self::WSS(di) => di.socket_address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_ip_addr(&self) -> IpAddr {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address.to_ip_addr(),
|
||||||
|
Self::TCP(di) => di.socket_address.to_ip_addr(),
|
||||||
|
Self::WS(di) => di.socket_address.to_ip_addr(),
|
||||||
|
Self::WSS(di) => di.socket_address.to_ip_addr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address.port(),
|
||||||
|
Self::TCP(di) => di.socket_address.port(),
|
||||||
|
Self::WS(di) => di.socket_address.port(),
|
||||||
|
Self::WSS(di) => di.socket_address.port(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_port(&mut self, port: u16) {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address.set_port(port),
|
||||||
|
Self::TCP(di) => di.socket_address.set_port(port),
|
||||||
|
Self::WS(di) => di.socket_address.set_port(port),
|
||||||
|
Self::WSS(di) => di.socket_address.set_port(port),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_socket_addr(&self) -> SocketAddr {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address.to_socket_addr(),
|
||||||
|
Self::TCP(di) => di.socket_address.to_socket_addr(),
|
||||||
|
Self::WS(di) => di.socket_address.to_socket_addr(),
|
||||||
|
Self::WSS(di) => di.socket_address.to_socket_addr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_peer_address(&self) -> PeerAddress {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => PeerAddress::new(di.socket_address, ProtocolType::UDP),
|
||||||
|
Self::TCP(di) => PeerAddress::new(di.socket_address, ProtocolType::TCP),
|
||||||
|
Self::WS(di) => PeerAddress::new(di.socket_address, ProtocolType::WS),
|
||||||
|
Self::WSS(di) => PeerAddress::new(di.socket_address, ProtocolType::WSS),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn request(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Self::UDP(_) => None,
|
||||||
|
Self::TCP(_) => None,
|
||||||
|
Self::WS(di) => Some(format!("ws://{}", di.request)),
|
||||||
|
Self::WSS(di) => Some(format!("wss://{}", di.request)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
let socket_address = self.socket_address();
|
||||||
|
let address = socket_address.address();
|
||||||
|
let port = socket_address.port();
|
||||||
|
(address.is_global() || address.is_local()) && port > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_filter(&self) -> DialInfoFilter {
|
||||||
|
DialInfoFilter {
|
||||||
|
protocol_type_set: ProtocolTypeSet::only(self.protocol_type()),
|
||||||
|
address_type_set: AddressTypeSet::only(self.address_type()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_vec_from_short<S: AsRef<str>, H: AsRef<str>>(
|
||||||
|
short: S,
|
||||||
|
hostname: H,
|
||||||
|
) -> Result<Vec<Self>, VeilidAPIError> {
|
||||||
|
let short = short.as_ref();
|
||||||
|
let hostname = hostname.as_ref();
|
||||||
|
|
||||||
|
if short.len() < 2 {
|
||||||
|
apibail_parse_error!("invalid short url length", short);
|
||||||
|
}
|
||||||
|
let url = match &short[0..1] {
|
||||||
|
"U" => {
|
||||||
|
format!("udp://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
"T" => {
|
||||||
|
format!("tcp://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
"W" => {
|
||||||
|
format!("ws://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
"S" => {
|
||||||
|
format!("wss://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
apibail_parse_error!("invalid short url type", short);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Self::try_vec_from_url(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_vec_from_url<S: AsRef<str>>(url: S) -> Result<Vec<Self>, VeilidAPIError> {
|
||||||
|
let url = url.as_ref();
|
||||||
|
let split_url = SplitUrl::from_str(url)
|
||||||
|
.map_err(|e| VeilidAPIError::parse_error(format!("unable to split url: {}", e), url))?;
|
||||||
|
|
||||||
|
let port = match split_url.scheme.as_str() {
|
||||||
|
"udp" | "tcp" => split_url
|
||||||
|
.port
|
||||||
|
.ok_or_else(|| VeilidAPIError::parse_error("Missing port in udp url", url))?,
|
||||||
|
"ws" => split_url.port.unwrap_or(80u16),
|
||||||
|
"wss" => split_url.port.unwrap_or(443u16),
|
||||||
|
_ => {
|
||||||
|
apibail_parse_error!("Invalid dial info url scheme", split_url.scheme);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let socket_addrs = {
|
||||||
|
// Resolve if possible, WASM doesn't support resolution and doesn't need it to connect to the dialinfo
|
||||||
|
// This will not be used on signed dialinfo, only for bootstrapping, so we don't need to worry about
|
||||||
|
// the '0.0.0.0' address being propagated across the routing table
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_arch = "wasm32")] {
|
||||||
|
vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0,0,0,0)), port)]
|
||||||
|
} else {
|
||||||
|
match split_url.host {
|
||||||
|
SplitUrlHost::Hostname(_) => split_url
|
||||||
|
.host_port(port)
|
||||||
|
.to_socket_addrs()
|
||||||
|
.map_err(|_| VeilidAPIError::parse_error("couldn't resolve hostname in url", url))?
|
||||||
|
.collect(),
|
||||||
|
SplitUrlHost::IpAddr(a) => vec![SocketAddr::new(a, port)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for sa in socket_addrs {
|
||||||
|
out.push(match split_url.scheme.as_str() {
|
||||||
|
"udp" => Self::udp_from_socketaddr(sa),
|
||||||
|
"tcp" => Self::tcp_from_socketaddr(sa),
|
||||||
|
"ws" => Self::try_ws(
|
||||||
|
SocketAddress::from_socket_addr(sa).to_canonical(),
|
||||||
|
url.to_string(),
|
||||||
|
)?,
|
||||||
|
"wss" => Self::try_wss(
|
||||||
|
SocketAddress::from_socket_addr(sa).to_canonical(),
|
||||||
|
url.to_string(),
|
||||||
|
)?,
|
||||||
|
_ => {
|
||||||
|
unreachable!("Invalid dial info url scheme")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn to_short(&self) -> (String, String) {
|
||||||
|
match self {
|
||||||
|
DialInfo::UDP(di) => (
|
||||||
|
format!("U{}", di.socket_address.port()),
|
||||||
|
intf::ptr_lookup(di.socket_address.to_ip_addr())
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| di.socket_address.to_string()),
|
||||||
|
),
|
||||||
|
DialInfo::TCP(di) => (
|
||||||
|
format!("T{}", di.socket_address.port()),
|
||||||
|
intf::ptr_lookup(di.socket_address.to_ip_addr())
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| di.socket_address.to_string()),
|
||||||
|
),
|
||||||
|
DialInfo::WS(di) => {
|
||||||
|
let mut split_url = SplitUrl::from_str(&format!("ws://{}", di.request)).unwrap();
|
||||||
|
if let SplitUrlHost::IpAddr(a) = split_url.host {
|
||||||
|
if let Ok(host) = intf::ptr_lookup(a).await {
|
||||||
|
split_url.host = SplitUrlHost::Hostname(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"W{}{}",
|
||||||
|
split_url.port.unwrap_or(80),
|
||||||
|
split_url
|
||||||
|
.path
|
||||||
|
.map(|p| format!("/{}", p))
|
||||||
|
.unwrap_or_default()
|
||||||
|
),
|
||||||
|
split_url.host.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DialInfo::WSS(di) => {
|
||||||
|
let mut split_url = SplitUrl::from_str(&format!("wss://{}", di.request)).unwrap();
|
||||||
|
if let SplitUrlHost::IpAddr(a) = split_url.host {
|
||||||
|
if let Ok(host) = intf::ptr_lookup(a).await {
|
||||||
|
split_url.host = SplitUrlHost::Hostname(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"S{}{}",
|
||||||
|
split_url.port.unwrap_or(443),
|
||||||
|
split_url
|
||||||
|
.path
|
||||||
|
.map(|p| format!("/{}", p))
|
||||||
|
.unwrap_or_default()
|
||||||
|
),
|
||||||
|
split_url.host.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn to_url(&self) -> String {
|
||||||
|
match self {
|
||||||
|
DialInfo::UDP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr())
|
||||||
|
.await
|
||||||
|
.map(|h| format!("udp://{}:{}", h, di.socket_address.port()))
|
||||||
|
.unwrap_or_else(|_| format!("udp://{}", di.socket_address)),
|
||||||
|
DialInfo::TCP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr())
|
||||||
|
.await
|
||||||
|
.map(|h| format!("tcp://{}:{}", h, di.socket_address.port()))
|
||||||
|
.unwrap_or_else(|_| format!("tcp://{}", di.socket_address)),
|
||||||
|
DialInfo::WS(di) => {
|
||||||
|
let mut split_url = SplitUrl::from_str(&format!("ws://{}", di.request)).unwrap();
|
||||||
|
if let SplitUrlHost::IpAddr(a) = split_url.host {
|
||||||
|
if let Ok(host) = intf::ptr_lookup(a).await {
|
||||||
|
split_url.host = SplitUrlHost::Hostname(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
split_url.to_string()
|
||||||
|
}
|
||||||
|
DialInfo::WSS(di) => {
|
||||||
|
let mut split_url = SplitUrl::from_str(&format!("wss://{}", di.request)).unwrap();
|
||||||
|
if let SplitUrlHost::IpAddr(a) = split_url.host {
|
||||||
|
if let Ok(host) = intf::ptr_lookup(a).await {
|
||||||
|
split_url.host = SplitUrlHost::Hostname(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
split_url.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ordered_sequencing_sort(a: &DialInfo, b: &DialInfo) -> core::cmp::Ordering {
|
||||||
|
let ca = a.protocol_type().sort_order(Sequencing::EnsureOrdered);
|
||||||
|
let cb = b.protocol_type().sort_order(Sequencing::EnsureOrdered);
|
||||||
|
if ca < cb {
|
||||||
|
return core::cmp::Ordering::Less;
|
||||||
|
}
|
||||||
|
if ca > cb {
|
||||||
|
return core::cmp::Ordering::Greater;
|
||||||
|
}
|
||||||
|
match (a, b) {
|
||||||
|
(DialInfo::UDP(a), DialInfo::UDP(b)) => a.cmp(b),
|
||||||
|
(DialInfo::TCP(a), DialInfo::TCP(b)) => a.cmp(b),
|
||||||
|
(DialInfo::WS(a), DialInfo::WS(b)) => a.cmp(b),
|
||||||
|
(DialInfo::WSS(a), DialInfo::WSS(b)) => a.cmp(b),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MatchesDialInfoFilter for DialInfo {
|
||||||
|
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
||||||
|
if !filter.protocol_type_set.contains(self.protocol_type()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !filter.address_type_set.contains(self.address_type()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
21
veilid-core/src/network_manager/types/dial_info/tcp.rs
Normal file
21
veilid-core/src/network_manager/types/dial_info/tcp.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DialInfoTCP {
|
||||||
|
pub socket_address: SocketAddress,
|
||||||
|
}
|
21
veilid-core/src/network_manager/types/dial_info/udp.rs
Normal file
21
veilid-core/src/network_manager/types/dial_info/udp.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DialInfoUDP {
|
||||||
|
pub socket_address: SocketAddress,
|
||||||
|
}
|
22
veilid-core/src/network_manager/types/dial_info/ws.rs
Normal file
22
veilid-core/src/network_manager/types/dial_info/ws.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DialInfoWS {
|
||||||
|
pub socket_address: SocketAddress,
|
||||||
|
pub request: String,
|
||||||
|
}
|
22
veilid-core/src/network_manager/types/dial_info/wss.rs
Normal file
22
veilid-core/src/network_manager/types/dial_info/wss.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DialInfoWSS {
|
||||||
|
pub socket_address: SocketAddress,
|
||||||
|
pub request: String,
|
||||||
|
}
|
50
veilid-core/src/network_manager/types/dial_info_class.rs
Normal file
50
veilid-core/src/network_manager/types/dial_info_class.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Keep member order appropriate for sorting < preference
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum DialInfoClass {
|
||||||
|
Direct = 0, // D = Directly reachable with public IP and no firewall, with statically configured port
|
||||||
|
Mapped = 1, // M = Directly reachable with via portmap behind any NAT or firewalled with dynamically negotiated port
|
||||||
|
FullConeNAT = 2, // F = Directly reachable device without portmap behind full-cone NAT
|
||||||
|
Blocked = 3, // B = Inbound blocked at firewall but may hole punch with public address
|
||||||
|
AddressRestrictedNAT = 4, // A = Device without portmap behind address-only restricted NAT
|
||||||
|
PortRestrictedNAT = 5, // P = Device without portmap behind address-and-port restricted NAT
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DialInfoClass {
|
||||||
|
// Is a signal required to do an inbound hole-punch?
|
||||||
|
pub fn requires_signal(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Blocked | Self::AddressRestrictedNAT | Self::PortRestrictedNAT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does a relay node need to be allocated for this dial info?
|
||||||
|
// For full cone NAT, the relay itself may not be used but the keepalive sent to it
|
||||||
|
// is required to keep the NAT mapping valid in the router state table
|
||||||
|
pub fn requires_relay(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::FullConeNAT
|
||||||
|
| Self::Blocked
|
||||||
|
| Self::AddressRestrictedNAT
|
||||||
|
| Self::PortRestrictedNAT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
86
veilid-core/src/network_manager/types/dial_info_filter.rs
Normal file
86
veilid-core/src/network_manager/types/dial_info_filter.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DialInfoFilter {
|
||||||
|
#[with(RkyvEnumSet)]
|
||||||
|
pub protocol_type_set: ProtocolTypeSet,
|
||||||
|
#[with(RkyvEnumSet)]
|
||||||
|
pub address_type_set: AddressTypeSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DialInfoFilter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
protocol_type_set: ProtocolTypeSet::all(),
|
||||||
|
address_type_set: AddressTypeSet::all(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DialInfoFilter {
|
||||||
|
pub fn all() -> Self {
|
||||||
|
Self {
|
||||||
|
protocol_type_set: ProtocolTypeSet::all(),
|
||||||
|
address_type_set: AddressTypeSet::all(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_protocol_type(mut self, protocol_type: ProtocolType) -> Self {
|
||||||
|
self.protocol_type_set = ProtocolTypeSet::only(protocol_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_protocol_type_set(mut self, protocol_set: ProtocolTypeSet) -> Self {
|
||||||
|
self.protocol_type_set = protocol_set;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_address_type(mut self, address_type: AddressType) -> Self {
|
||||||
|
self.address_type_set = AddressTypeSet::only(address_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_address_type_set(mut self, address_set: AddressTypeSet) -> Self {
|
||||||
|
self.address_type_set = address_set;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn filtered(mut self, other_dif: &DialInfoFilter) -> Self {
|
||||||
|
self.protocol_type_set &= other_dif.protocol_type_set;
|
||||||
|
self.address_type_set &= other_dif.address_type_set;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn is_dead(&self) -> bool {
|
||||||
|
self.protocol_type_set.is_empty() || self.address_type_set.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for DialInfoFilter {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
let mut out = String::new();
|
||||||
|
if self.protocol_type_set != ProtocolTypeSet::all() {
|
||||||
|
out += &format!("+{:?}", self.protocol_type_set);
|
||||||
|
} else {
|
||||||
|
out += "*";
|
||||||
|
}
|
||||||
|
if self.address_type_set != AddressTypeSet::all() {
|
||||||
|
out += &format!("+{:?}", self.address_type_set);
|
||||||
|
} else {
|
||||||
|
out += "*";
|
||||||
|
}
|
||||||
|
write!(f, "[{}]", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MatchesDialInfoFilter {
|
||||||
|
fn matches_filter(&self, filter: &DialInfoFilter) -> bool;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Keep member order appropriate for sorting < preference
|
||||||
|
// Must match DialInfo order
|
||||||
|
#[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 LowLevelProtocolType {
|
||||||
|
UDP,
|
||||||
|
TCP,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LowLevelProtocolType {
|
||||||
|
pub fn is_connection_oriented(&self) -> bool {
|
||||||
|
matches!(self, LowLevelProtocolType::TCP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type LowLevelProtocolTypeSet = EnumSet<LowLevelProtocolType>;
|
||||||
|
|
31
veilid-core/src/network_manager/types/mod.rs
Normal file
31
veilid-core/src/network_manager/types/mod.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
mod address;
|
||||||
|
mod address_type;
|
||||||
|
mod connection_descriptor;
|
||||||
|
mod dial_info;
|
||||||
|
mod dial_info_class;
|
||||||
|
mod dial_info_filter;
|
||||||
|
mod low_level_protocol_type;
|
||||||
|
mod network_class;
|
||||||
|
mod peer_address;
|
||||||
|
mod protocol_type;
|
||||||
|
mod signal_info;
|
||||||
|
mod socket_address;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use address::*;
|
||||||
|
pub use address_type::*;
|
||||||
|
pub use connection_descriptor::*;
|
||||||
|
pub use dial_info::*;
|
||||||
|
pub use dial_info_class::*;
|
||||||
|
pub use dial_info_filter::*;
|
||||||
|
pub use low_level_protocol_type::*;
|
||||||
|
pub use network_class::*;
|
||||||
|
pub use peer_address::*;
|
||||||
|
pub use protocol_type::*;
|
||||||
|
pub use signal_info::*;
|
||||||
|
pub use socket_address::*;
|
||||||
|
|
||||||
|
use enumset::*;
|
||||||
|
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||||
|
use serde::*;
|
37
veilid-core/src/network_manager/types/network_class.rs
Normal file
37
veilid-core/src/network_manager/types/network_class.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum NetworkClass {
|
||||||
|
InboundCapable = 0, // I = Inbound capable without relay, may require signal
|
||||||
|
OutboundOnly = 1, // O = Outbound only, inbound relay required except with reverse connect signal
|
||||||
|
WebApp = 2, // W = PWA, outbound relay is required in most cases
|
||||||
|
Invalid = 3, // X = Invalid network class, we don't know how to reach this node
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NetworkClass {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkClass {
|
||||||
|
// Should an outbound relay be kept available?
|
||||||
|
pub fn outbound_wants_relay(&self) -> bool {
|
||||||
|
matches!(self, Self::WebApp)
|
||||||
|
}
|
||||||
|
}
|
66
veilid-core/src/network_manager/types/peer_address.rs
Normal file
66
veilid-core/src/network_manager/types/peer_address.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct PeerAddress {
|
||||||
|
protocol_type: ProtocolType,
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
socket_address: SocketAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PeerAddress {
|
||||||
|
pub fn new(socket_address: SocketAddress, protocol_type: ProtocolType) -> Self {
|
||||||
|
Self {
|
||||||
|
socket_address: socket_address.to_canonical(),
|
||||||
|
protocol_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn socket_address(&self) -> &SocketAddress {
|
||||||
|
&self.socket_address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn protocol_type(&self) -> ProtocolType {
|
||||||
|
self.protocol_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_socket_addr(&self) -> SocketAddr {
|
||||||
|
self.socket_address.to_socket_addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address_type(&self) -> AddressType {
|
||||||
|
self.socket_address.address_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PeerAddress {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}:{}", self.protocol_type, self.socket_address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PeerAddress {
|
||||||
|
type Err = VeilidAPIError;
|
||||||
|
fn from_str(s: &str) -> Result<PeerAddress, VeilidAPIError> {
|
||||||
|
let Some((first, second)) = s.split_once(':') else {
|
||||||
|
return Err(VeilidAPIError::parse_error("PeerAddress is missing a colon: {}", s));
|
||||||
|
};
|
||||||
|
let protocol_type = ProtocolType::from_str(first)?;
|
||||||
|
let socket_address = SocketAddress::from_str(second)?;
|
||||||
|
Ok(PeerAddress::new(socket_address, protocol_type))
|
||||||
|
}
|
||||||
|
}
|
104
veilid-core/src/network_manager/types/protocol_type.rs
Normal file
104
veilid-core/src/network_manager/types/protocol_type.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Keep member order appropriate for sorting < preference
|
||||||
|
// Must match DialInfo order
|
||||||
|
#[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 ProtocolType {
|
||||||
|
UDP,
|
||||||
|
TCP,
|
||||||
|
WS,
|
||||||
|
WSS,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProtocolType {
|
||||||
|
pub fn is_connection_oriented(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn low_level_protocol_type(&self) -> LowLevelProtocolType {
|
||||||
|
match self {
|
||||||
|
ProtocolType::UDP => LowLevelProtocolType::UDP,
|
||||||
|
ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS => LowLevelProtocolType::TCP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn sort_order(&self, sequencing: Sequencing) -> usize {
|
||||||
|
match self {
|
||||||
|
ProtocolType::UDP => {
|
||||||
|
if sequencing != Sequencing::NoPreference {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProtocolType::TCP => {
|
||||||
|
if sequencing != Sequencing::NoPreference {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProtocolType::WS => {
|
||||||
|
if sequencing != Sequencing::NoPreference {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProtocolType::WSS => {
|
||||||
|
if sequencing != Sequencing::NoPreference {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn all_ordered_set() -> ProtocolTypeSet {
|
||||||
|
ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ProtocolType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ProtocolType::UDP => write!(f, "UDP"),
|
||||||
|
ProtocolType::TCP => write!(f, "TCP"),
|
||||||
|
ProtocolType::WS => write!(f, "WS"),
|
||||||
|
ProtocolType::WSS => write!(f, "WSS"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ProtocolType {
|
||||||
|
type Err = VeilidAPIError;
|
||||||
|
fn from_str(s: &str) -> Result<ProtocolType, VeilidAPIError> {
|
||||||
|
match s.to_ascii_uppercase().as_str() {
|
||||||
|
"UDP" => Ok(ProtocolType::UDP),
|
||||||
|
"TCP" => Ok(ProtocolType::TCP),
|
||||||
|
"WS" => Ok(ProtocolType::WS),
|
||||||
|
"WSS" => Ok(ProtocolType::WSS),
|
||||||
|
_ => Err(VeilidAPIError::parse_error(
|
||||||
|
"ProtocolType::from_str failed",
|
||||||
|
s,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ProtocolTypeSet = EnumSet<ProtocolType>;
|
22
veilid-core/src/network_manager/types/signal_info.rs
Normal file
22
veilid-core/src/network_manager/types/signal_info.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Parameter for Signal operation
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum SignalInfo {
|
||||||
|
/// UDP Hole Punch Request
|
||||||
|
HolePunch {
|
||||||
|
/// /// Receipt to be returned after the hole punch
|
||||||
|
receipt: Vec<u8>,
|
||||||
|
/// Sender's peer info
|
||||||
|
peer_info: PeerInfo,
|
||||||
|
},
|
||||||
|
/// Reverse Connection Request
|
||||||
|
ReverseConnect {
|
||||||
|
/// Receipt to be returned by the reverse connection
|
||||||
|
receipt: Vec<u8>,
|
||||||
|
/// Sender's peer info
|
||||||
|
peer_info: PeerInfo,
|
||||||
|
},
|
||||||
|
// XXX: WebRTC
|
||||||
|
}
|
77
veilid-core/src/network_manager/types/socket_address.rs
Normal file
77
veilid-core/src/network_manager/types/socket_address.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct SocketAddress {
|
||||||
|
address: Address,
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SocketAddress {
|
||||||
|
pub fn new(address: Address, port: u16) -> Self {
|
||||||
|
Self { address, port }
|
||||||
|
}
|
||||||
|
pub fn from_socket_addr(sa: SocketAddr) -> SocketAddress {
|
||||||
|
Self {
|
||||||
|
address: Address::from_socket_addr(sa),
|
||||||
|
port: sa.port(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn address(&self) -> Address {
|
||||||
|
self.address
|
||||||
|
}
|
||||||
|
pub fn set_address(&mut self, address: Address) {
|
||||||
|
self.address = address;
|
||||||
|
}
|
||||||
|
pub fn address_type(&self) -> AddressType {
|
||||||
|
self.address.address_type()
|
||||||
|
}
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
|
pub fn set_port(&mut self, port: u16) {
|
||||||
|
self.port = port
|
||||||
|
}
|
||||||
|
pub fn to_canonical(&self) -> SocketAddress {
|
||||||
|
SocketAddress {
|
||||||
|
address: self.address.to_canonical(),
|
||||||
|
port: self.port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_ip_addr(&self) -> IpAddr {
|
||||||
|
self.address.to_ip_addr()
|
||||||
|
}
|
||||||
|
pub fn to_socket_addr(&self) -> SocketAddr {
|
||||||
|
self.address.to_socket_addr(self.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SocketAddress {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", self.to_socket_addr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for SocketAddress {
|
||||||
|
type Err = VeilidAPIError;
|
||||||
|
fn from_str(s: &str) -> Result<SocketAddress, VeilidAPIError> {
|
||||||
|
let sa = SocketAddr::from_str(s)
|
||||||
|
.map_err(|e| VeilidAPIError::parse_error("Failed to parse SocketAddress", e))?;
|
||||||
|
Ok(SocketAddress::from_socket_addr(sa))
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ mod routing_domains;
|
|||||||
mod routing_table_inner;
|
mod routing_table_inner;
|
||||||
mod stats_accounting;
|
mod stats_accounting;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
|
mod types;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
@ -17,9 +18,10 @@ use crate::crypto::*;
|
|||||||
use crate::network_manager::*;
|
use crate::network_manager::*;
|
||||||
use crate::rpc_processor::*;
|
use crate::rpc_processor::*;
|
||||||
use bucket::*;
|
use bucket::*;
|
||||||
|
use hashlink::LruCache;
|
||||||
|
|
||||||
pub use bucket_entry::*;
|
pub use bucket_entry::*;
|
||||||
pub use debug::*;
|
pub use debug::*;
|
||||||
use hashlink::LruCache;
|
|
||||||
pub use node_ref::*;
|
pub use node_ref::*;
|
||||||
pub use node_ref_filter::*;
|
pub use node_ref_filter::*;
|
||||||
pub use privacy::*;
|
pub use privacy::*;
|
||||||
@ -28,6 +30,7 @@ pub use routing_domain_editor::*;
|
|||||||
pub use routing_domains::*;
|
pub use routing_domains::*;
|
||||||
pub use routing_table_inner::*;
|
pub use routing_table_inner::*;
|
||||||
pub use stats_accounting::*;
|
pub use stats_accounting::*;
|
||||||
|
pub use types::*;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
43
veilid-core/src/routing_table/types/dial_info_detail.rs
Normal file
43
veilid-core/src/routing_table/types/dial_info_detail.rs
Normal 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>;
|
||||||
|
}
|
22
veilid-core/src/routing_table/types/direction.rs
Normal file
22
veilid-core/src/routing_table/types/direction.rs
Normal 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>;
|
25
veilid-core/src/routing_table/types/mod.rs
Normal file
25
veilid-core/src/routing_table/types/mod.rs
Normal 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::*;
|
125
veilid-core/src/routing_table/types/node_info.rs
Normal file
125
veilid-core/src/routing_table/types/node_info.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
66
veilid-core/src/routing_table/types/node_status.rs
Normal file
66
veilid-core/src/routing_table/types/node_status.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
veilid-core/src/routing_table/types/peer_info.rs
Normal file
18
veilid-core/src/routing_table/types/peer_info.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
veilid-core/src/routing_table/types/routing_domain.rs
Normal file
32
veilid-core/src/routing_table/types/routing_domain.rs
Normal 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>;
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
89
veilid-core/src/routing_table/types/signed_node_info.rs
Normal file
89
veilid-core/src/routing_table/types/signed_node_info.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
106
veilid-core/src/routing_table/types/signed_relayed_node_info.rs
Normal file
106
veilid-core/src/routing_table/types/signed_relayed_node_info.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -19,11 +19,13 @@ mod signature512;
|
|||||||
mod signed_direct_node_info;
|
mod signed_direct_node_info;
|
||||||
mod signed_node_info;
|
mod signed_node_info;
|
||||||
mod signed_relayed_node_info;
|
mod signed_relayed_node_info;
|
||||||
|
mod signed_value_data;
|
||||||
|
mod signed_value_descriptor;
|
||||||
mod socket_address;
|
mod socket_address;
|
||||||
mod tunnel;
|
mod tunnel;
|
||||||
mod typed_key;
|
mod typed_key;
|
||||||
mod typed_signature;
|
mod typed_signature;
|
||||||
mod value_data;
|
mod value_detail;
|
||||||
|
|
||||||
pub use address::*;
|
pub use address::*;
|
||||||
pub use address_type_set::*;
|
pub use address_type_set::*;
|
||||||
@ -46,10 +48,12 @@ pub use signature512::*;
|
|||||||
pub use signed_direct_node_info::*;
|
pub use signed_direct_node_info::*;
|
||||||
pub use signed_node_info::*;
|
pub use signed_node_info::*;
|
||||||
pub use signed_relayed_node_info::*;
|
pub use signed_relayed_node_info::*;
|
||||||
|
pub use signed_value_data::*;
|
||||||
|
pub use signed_value_descriptor::*;
|
||||||
pub use socket_address::*;
|
pub use socket_address::*;
|
||||||
pub use tunnel::*;
|
pub use tunnel::*;
|
||||||
pub use typed_key::*;
|
pub use typed_key::*;
|
||||||
pub use typed_signature::*;
|
pub use typed_signature::*;
|
||||||
pub use value_data::*;
|
pub use value_detail::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
31
veilid-core/src/rpc_processor/coders/signed_value_data.rs
Normal file
31
veilid-core/src/rpc_processor/coders/signed_value_data.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::storage_manager::*;
|
||||||
|
|
||||||
|
pub fn encode_signed_value_data(
|
||||||
|
signed_value_data: &SignedValueData,
|
||||||
|
builder: &mut veilid_capnp::signed_value_data::Builder,
|
||||||
|
) -> Result<(), RPCError> {
|
||||||
|
builder.set_seq(signed_value_data.value_data().seq());
|
||||||
|
builder.set_data(signed_value_data.value_data().data());
|
||||||
|
let mut wb = builder.reborrow().init_writer();
|
||||||
|
encode_key256(signed_value_data.value_data().writer(), &mut wb);
|
||||||
|
let mut sb = builder.reborrow().init_signature();
|
||||||
|
encode_signature512(signed_value_data.signature(), &mut sb);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_signed_value_data(
|
||||||
|
reader: &veilid_capnp::signed_value_data::Reader,
|
||||||
|
) -> Result<ValueData, RPCError> {
|
||||||
|
let seq = reader.get_seq();
|
||||||
|
let data = reader.get_data().map_err(RPCError::protocol)?.to_vec();
|
||||||
|
let wr = reader.get_writer().map_err(RPCError::protocol)?;
|
||||||
|
let writer = decode_key256(&wr);
|
||||||
|
let sr = reader.get_signature().map_err(RPCError::protocol)?;
|
||||||
|
let signature = decode_signature512(&sr);
|
||||||
|
|
||||||
|
Ok(SignedValueData {
|
||||||
|
value_data: ValueData { seq, data, writer },
|
||||||
|
signature,
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::storage_manager::SignedValueDescriptor;
|
||||||
|
|
||||||
|
pub fn encode_signed_value_descriptor(
|
||||||
|
signed_value_descriptor: &SignedValueDescriptor,
|
||||||
|
builder: &mut veilid_capnp::signed_value_descriptor::Builder,
|
||||||
|
) -> Result<(), RPCError> {
|
||||||
|
let mut ob = builder.reborrow().init_owner();
|
||||||
|
encode_key256(signed_value_descriptor.owner(), &mut ob);
|
||||||
|
builder.set_data(signed_value_descriptor.data());
|
||||||
|
let mut sb = builder.reborrow().init_signature();
|
||||||
|
encode_signature512(signed_value_descriptor.signature(), &mut sb);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_signed_value_descriptor(
|
||||||
|
reader: &veilid_capnp::signed_value_descriptor::Reader,
|
||||||
|
vcrypto: CryptoSystemVersion,
|
||||||
|
) -> Result<SignedValueDescriptor, RPCError> {
|
||||||
|
let or = reader.get_owner().map_err(RPCError::protocol)?;
|
||||||
|
let owner = decode_key256(&or);
|
||||||
|
let data = reader.get_data().map_err(RPCError::protocol)?.to_vec();
|
||||||
|
let sr = reader.get_signature().map_err(RPCError::protocol)?;
|
||||||
|
let signature = decode_signature512(&sr);
|
||||||
|
Ok(SignedValueDescriptor::new(owner, data, signature, vcrypto).map_err(RPCError::protocol)?)
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn encode_value_data(
|
|
||||||
value_data: &ValueData,
|
|
||||||
builder: &mut veilid_capnp::value_data::Builder,
|
|
||||||
) -> Result<(), RPCError> {
|
|
||||||
builder.set_data(&value_data.data);
|
|
||||||
builder.set_schema(u32::from_be_bytes(value_data.schema.0));
|
|
||||||
builder.set_seq(value_data.seq);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode_value_data(reader: &veilid_capnp::value_data::Reader) -> Result<ValueData, RPCError> {
|
|
||||||
let data = reader.get_data().map_err(RPCError::protocol)?.to_vec();
|
|
||||||
let seq = reader.get_seq();
|
|
||||||
let schema = FourCC::from(reader.get_schema().to_be_bytes());
|
|
||||||
Ok(ValueData { data, schema, seq })
|
|
||||||
}
|
|
20
veilid-core/src/rpc_processor/coders/value_detail.rs
Normal file
20
veilid-core/src/rpc_processor/coders/value_detail.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::storage_manager::ValueDetail;
|
||||||
|
|
||||||
|
pub fn encode_value_detail(
|
||||||
|
value_detail: &ValueDetail,
|
||||||
|
builder: &mut veilid_capnp::value_detail::Builder,
|
||||||
|
) -> Result<(), RPCError> {
|
||||||
|
let mut svdb = builder.reborrow().init_signed_value_data();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_value_detail(
|
||||||
|
reader: &veilid_capnp::value_detail::Reader,
|
||||||
|
) -> Result<ValueDetail, RPCError> {
|
||||||
|
Ok(ValueDetail {
|
||||||
|
signed_value_data,
|
||||||
|
descriptor,
|
||||||
|
})
|
||||||
|
}
|
@ -387,7 +387,7 @@ impl RPCProcessor {
|
|||||||
_node_id: PublicKey,
|
_node_id: PublicKey,
|
||||||
_count: u32,
|
_count: u32,
|
||||||
_fanout: u32,
|
_fanout: u32,
|
||||||
_timeout: Option<u64>,
|
_timeout: TimestampDuration,
|
||||||
) -> Result<Option<NodeRef>, RPCError> {
|
) -> Result<Option<NodeRef>, RPCError> {
|
||||||
//let routing_table = self.routing_table();
|
//let routing_table = self.routing_table();
|
||||||
|
|
||||||
@ -402,7 +402,7 @@ impl RPCProcessor {
|
|||||||
_node_id: PublicKey,
|
_node_id: PublicKey,
|
||||||
_count: u32,
|
_count: u32,
|
||||||
_fanout: u32,
|
_fanout: u32,
|
||||||
_timeout: Option<u64>,
|
_timeout: TimestampDuration,
|
||||||
) -> Result<Vec<NodeRef>, RPCError> {
|
) -> Result<Vec<NodeRef>, RPCError> {
|
||||||
// xxx return closest nodes after the timeout
|
// xxx return closest nodes after the timeout
|
||||||
Err(RPCError::unimplemented("search_dht_multi_key")).map_err(logthru_rpc!(error))
|
Err(RPCError::unimplemented("search_dht_multi_key")).map_err(logthru_rpc!(error))
|
||||||
@ -433,7 +433,7 @@ impl RPCProcessor {
|
|||||||
(
|
(
|
||||||
c.network.dht.resolve_node_count,
|
c.network.dht.resolve_node_count,
|
||||||
c.network.dht.resolve_node_fanout,
|
c.network.dht.resolve_node_fanout,
|
||||||
c.network.dht.resolve_node_timeout_ms.map(ms_to_us),
|
TimestampDuration::from(ms_to_us(c.network.dht.resolve_node_timeout_ms)),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
mod keys;
|
mod keys;
|
||||||
|
mod record;
|
||||||
|
mod record_data;
|
||||||
mod record_store;
|
mod record_store;
|
||||||
mod record_store_limits;
|
mod record_store_limits;
|
||||||
mod value_record;
|
mod signed_value_data;
|
||||||
|
mod signed_value_descriptor;
|
||||||
|
mod value_detail;
|
||||||
|
|
||||||
use keys::*;
|
use keys::*;
|
||||||
|
use record::*;
|
||||||
|
use record_data::*;
|
||||||
use record_store::*;
|
use record_store::*;
|
||||||
use record_store_limits::*;
|
use record_store_limits::*;
|
||||||
use value_record::*;
|
|
||||||
|
pub use signed_value_data::*;
|
||||||
|
pub use signed_value_descriptor::*;
|
||||||
|
pub use value_detail::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::rpc_processor::*;
|
use crate::rpc_processor::*;
|
||||||
@ -150,12 +159,28 @@ impl StorageManager {
|
|||||||
debug!("finished storage manager shutdown");
|
debug!("finished storage manager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn new_local_record(&self, key: TypedKey, record: Record) -> Result<(), VeilidAPIError> {
|
/// # DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ]
|
||||||
|
fn get_key(&self, vcrypto: CryptoSystemVersion, record: &Record) -> TypedKey {
|
||||||
|
let compiled = record.descriptor().schema_data();
|
||||||
|
let mut hash_data = Vec::<u8>::with_capacity(PUBLIC_KEY_LENGTH + 4 + compiled.len());
|
||||||
|
hash_data.extend_from_slice(&vcrypto.kind().0);
|
||||||
|
hash_data.extend_from_slice(&record.owner().bytes);
|
||||||
|
hash_data.extend_from_slice(compiled);
|
||||||
|
let hash = vcrypto.generate_hash(&hash_data);
|
||||||
|
TypedKey::new(vcrypto.kind(), hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new_local_record(
|
||||||
|
&self,
|
||||||
|
vcrypto: CryptoSystemVersion,
|
||||||
|
record: Record,
|
||||||
|
) -> Result<(), VeilidAPIError> {
|
||||||
// add value record to record store
|
// add value record to record store
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
let Some(local_record_store) = inner.local_record_store.as_mut() else {
|
let Some(local_record_store) = inner.local_record_store.as_mut() else {
|
||||||
apibail_generic!("not initialized");
|
apibail_generic!("not initialized");
|
||||||
};
|
};
|
||||||
|
let key = self.get_key(vcrypto.clone(), &record);
|
||||||
local_record_store.new_record(key, record).await
|
local_record_store.new_record(key, record).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,15 +195,25 @@ impl StorageManager {
|
|||||||
apibail_generic!("unsupported cryptosystem");
|
apibail_generic!("unsupported cryptosystem");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Compile the dht schema
|
||||||
|
let schema_data = schema.compile();
|
||||||
|
|
||||||
// New values require a new owner key
|
// New values require a new owner key
|
||||||
let keypair = vcrypto.generate_keypair();
|
let owner = vcrypto.generate_keypair();
|
||||||
let key = TypedKey::new(kind, keypair.key);
|
|
||||||
let secret = keypair.secret;
|
// Make a signed value descriptor for this dht value
|
||||||
|
let signed_value_descriptor = SignedValueDescriptor::new(owner.key, )
|
||||||
|
|
||||||
// Add new local value record
|
// Add new local value record
|
||||||
let cur_ts = get_aligned_timestamp();
|
let cur_ts = get_aligned_timestamp();
|
||||||
let record = Record::new(cur_ts, Some(secret), schema, safety_selection);
|
let record = Record::new(
|
||||||
self.new_local_record(key, record)
|
cur_ts,
|
||||||
|
owner.key,
|
||||||
|
Some(owner.secret),
|
||||||
|
schema,
|
||||||
|
safety_selection,
|
||||||
|
);
|
||||||
|
self.new_local_record(vcrypto.clone(), record)
|
||||||
.await
|
.await
|
||||||
.map_err(VeilidAPIError::internal)?;
|
.map_err(VeilidAPIError::internal)?;
|
||||||
|
|
||||||
|
@ -3,46 +3,15 @@ use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as
|
|||||||
use serde::*;
|
use serde::*;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
Debug,
|
|
||||||
Default,
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
Serialize,
|
|
||||||
Deserialize,
|
|
||||||
RkyvArchive,
|
|
||||||
RkyvSerialize,
|
|
||||||
RkyvDeserialize,
|
|
||||||
)]
|
|
||||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
|
||||||
pub struct RecordData {
|
|
||||||
pub value_data: ValueData,
|
|
||||||
pub signature: Signature,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordData {
|
|
||||||
pub fn total_size(&self) -> usize {
|
|
||||||
mem::size_of::<ValueData>() + self.value_data.data().len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Clone,
|
|
||||||
Debug,
|
|
||||||
Default,
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
Serialize,
|
|
||||||
Deserialize,
|
|
||||||
RkyvArchive,
|
|
||||||
RkyvSerialize,
|
|
||||||
RkyvDeserialize,
|
|
||||||
)]
|
)]
|
||||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
pub struct Record {
|
pub struct Record {
|
||||||
last_touched_ts: Timestamp,
|
last_touched_ts: Timestamp,
|
||||||
secret: Option<SecretKey>,
|
descriptor: SignedValueDescriptor,
|
||||||
schema: DHTSchema,
|
subkey_count: usize,
|
||||||
|
|
||||||
|
owner_secret: Option<SecretKey>,
|
||||||
safety_selection: SafetySelection,
|
safety_selection: SafetySelection,
|
||||||
record_data_size: usize,
|
record_data_size: usize,
|
||||||
}
|
}
|
||||||
@ -50,21 +19,31 @@ pub struct Record {
|
|||||||
impl Record {
|
impl Record {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cur_ts: Timestamp,
|
cur_ts: Timestamp,
|
||||||
secret: Option<SecretKey>,
|
descriptor: SignedValueDescriptor,
|
||||||
schema: DHTSchema,
|
owner_secret: Option<SecretKey>,
|
||||||
safety_selection: SafetySelection,
|
safety_selection: SafetySelection,
|
||||||
) -> Self {
|
) -> Result<Self, VeilidAPIError> {
|
||||||
Self {
|
let schema = descriptor.schema()?;
|
||||||
|
let subkey_count = schema.subkey_count();
|
||||||
|
Ok(Self {
|
||||||
last_touched_ts: cur_ts,
|
last_touched_ts: cur_ts,
|
||||||
secret,
|
descriptor,
|
||||||
schema,
|
subkey_count,
|
||||||
|
owner_secret,
|
||||||
safety_selection,
|
safety_selection,
|
||||||
record_data_size: 0,
|
record_data_size: 0,
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn descriptor(&self) -> &SignedValueDescriptor {
|
||||||
|
&self.descriptor
|
||||||
|
}
|
||||||
|
pub fn owner(&self) -> &PublicKey {
|
||||||
|
self.descriptor.owner()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subkey_count(&self) -> usize {
|
pub fn subkey_count(&self) -> usize {
|
||||||
self.schema.subkey_count()
|
self.subkey_count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn touch(&mut self, cur_ts: Timestamp) {
|
pub fn touch(&mut self, cur_ts: Timestamp) {
|
||||||
@ -83,7 +62,12 @@ impl Record {
|
|||||||
self.record_data_size
|
self.record_data_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn schema(&self) -> DHTSchema {
|
||||||
|
// unwrap is safe here because descriptor is immutable and set in new()
|
||||||
|
self.descriptor.schema().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn total_size(&self) -> usize {
|
pub fn total_size(&self) -> usize {
|
||||||
mem::size_of::<Record>() + self.schema.data_size() + self.record_data_size
|
mem::size_of::<Record>() + self.descriptor.total_size() + self.record_data_size
|
||||||
}
|
}
|
||||||
}
|
}
|
33
veilid-core/src/storage_manager/record_data.rs
Normal file
33
veilid-core/src/storage_manager/record_data.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use super::*;
|
||||||
|
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct RecordData {
|
||||||
|
signed_value_data: SignedValueData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordData {
|
||||||
|
pub fn new(signed_value_data: SignedValueData) -> Self {
|
||||||
|
Self { signed_value_data }
|
||||||
|
}
|
||||||
|
pub fn signed_value_data(&self) -> &SignedValueData {
|
||||||
|
&self.signed_value_data
|
||||||
|
}
|
||||||
|
pub fn total_size(&self) -> usize {
|
||||||
|
mem::size_of::<RecordData>() + self.signed_value_data.value_data().data().len()
|
||||||
|
}
|
||||||
|
}
|
@ -286,7 +286,7 @@ impl RecordStore {
|
|||||||
&mut self,
|
&mut self,
|
||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
) -> Result<Option<RecordData>, VeilidAPIError> {
|
) -> Result<Option<SignedValueData>, VeilidAPIError> {
|
||||||
// record from index
|
// record from index
|
||||||
let rtk = RecordTableKey { key };
|
let rtk = RecordTableKey { key };
|
||||||
let subkey_count = {
|
let subkey_count = {
|
||||||
@ -314,7 +314,7 @@ impl RecordStore {
|
|||||||
// If subkey exists in subkey cache, use that
|
// If subkey exists in subkey cache, use that
|
||||||
let stk = SubkeyTableKey { key, subkey };
|
let stk = SubkeyTableKey { key, subkey };
|
||||||
if let Some(record_data) = self.subkey_cache.get_mut(&stk) {
|
if let Some(record_data) = self.subkey_cache.get_mut(&stk) {
|
||||||
let out = record_data.clone();
|
let out = record_data.signed_value_data().clone();
|
||||||
|
|
||||||
return Ok(Some(out));
|
return Ok(Some(out));
|
||||||
}
|
}
|
||||||
@ -323,7 +323,7 @@ impl RecordStore {
|
|||||||
.load_rkyv::<RecordData>(0, &stk.bytes())
|
.load_rkyv::<RecordData>(0, &stk.bytes())
|
||||||
.map_err(VeilidAPIError::internal)?
|
.map_err(VeilidAPIError::internal)?
|
||||||
{
|
{
|
||||||
let out = record_data.clone();
|
let out = record_data.signed_value_data().clone();
|
||||||
|
|
||||||
// Add to cache, do nothing with lru out
|
// Add to cache, do nothing with lru out
|
||||||
self.add_to_subkey_cache(stk, record_data);
|
self.add_to_subkey_cache(stk, record_data);
|
||||||
@ -338,10 +338,10 @@ impl RecordStore {
|
|||||||
&mut self,
|
&mut self,
|
||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
record_data: RecordData,
|
signed_value_data: SignedValueData,
|
||||||
) -> Result<(), VeilidAPIError> {
|
) -> Result<(), VeilidAPIError> {
|
||||||
// Check size limit for data
|
// Check size limit for data
|
||||||
if record_data.value_data.data().len() > self.limits.max_subkey_size {
|
if signed_value_data.value_data().data().len() > self.limits.max_subkey_size {
|
||||||
return Err(VeilidAPIError::generic("record subkey too large"));
|
return Err(VeilidAPIError::generic("record subkey too large"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,6 +388,9 @@ impl RecordStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make new record data
|
||||||
|
let record_data = RecordData::new(signed_value_data);
|
||||||
|
|
||||||
// Check new total record size
|
// Check new total record size
|
||||||
let new_record_data_size = record_data.total_size();
|
let new_record_data_size = record_data.total_size();
|
||||||
let new_total_size = total_size + new_record_data_size - prior_record_data_size;
|
let new_total_size = total_size + new_record_data_size - prior_record_data_size;
|
||||||
|
92
veilid-core/src/storage_manager/signed_value_data.rs
Normal file
92
veilid-core/src/storage_manager/signed_value_data.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use super::*;
|
||||||
|
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialOrd,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct SignedValueData {
|
||||||
|
value_data: ValueData,
|
||||||
|
signature: Signature,
|
||||||
|
}
|
||||||
|
impl SignedValueData {
|
||||||
|
pub fn new(
|
||||||
|
value_data: ValueData,
|
||||||
|
owner: PublicKey,
|
||||||
|
subkey: ValueSubkey,
|
||||||
|
signature: Signature,
|
||||||
|
vcrypto: CryptoSystemVersion,
|
||||||
|
) -> Result<Self, VeilidAPIError> {
|
||||||
|
let node_info_bytes = Self::make_signature_bytes(&value_data, &owner, subkey)?;
|
||||||
|
|
||||||
|
// validate signature
|
||||||
|
vcrypto.verify(&value_data.writer(), &node_info_bytes, &signature)?;
|
||||||
|
Ok(Self {
|
||||||
|
value_data,
|
||||||
|
signature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_signature(
|
||||||
|
value_data: ValueData,
|
||||||
|
owner: PublicKey,
|
||||||
|
subkey: ValueSubkey,
|
||||||
|
vcrypto: CryptoSystemVersion,
|
||||||
|
writer_secret: SecretKey,
|
||||||
|
) -> Result<Self, VeilidAPIError> {
|
||||||
|
let node_info_bytes = Self::make_signature_bytes(&value_data, &owner, subkey)?;
|
||||||
|
|
||||||
|
// create signature
|
||||||
|
let signature = vcrypto.sign(&value_data.writer(), &writer_secret, &node_info_bytes)?;
|
||||||
|
Ok(Self {
|
||||||
|
value_data,
|
||||||
|
signature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value_data(&self) -> &ValueData {
|
||||||
|
&self.value_data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self) -> &Signature {
|
||||||
|
&self.signature
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_size(&self) -> usize {
|
||||||
|
(mem::size_of::<Self>() - mem::size_of::<ValueData>()) + self.value_data.total_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_signature_bytes(
|
||||||
|
value_data: &ValueData,
|
||||||
|
owner: &PublicKey,
|
||||||
|
subkey: ValueSubkey,
|
||||||
|
) -> Result<Vec<u8>, VeilidAPIError> {
|
||||||
|
let mut node_info_bytes =
|
||||||
|
Vec::with_capacity(PUBLIC_KEY_LENGTH + 4 + 4 + value_data.data().len());
|
||||||
|
|
||||||
|
// Add owner to signature
|
||||||
|
node_info_bytes.extend_from_slice(&owner.bytes);
|
||||||
|
// Add subkey to signature
|
||||||
|
node_info_bytes.extend_from_slice(&subkey.to_le_bytes());
|
||||||
|
// Add sequence number to signature
|
||||||
|
node_info_bytes.extend_from_slice(&value_data.seq().to_le_bytes());
|
||||||
|
// Add data to signature
|
||||||
|
node_info_bytes.extend_from_slice(value_data.data());
|
||||||
|
|
||||||
|
Ok(node_info_bytes)
|
||||||
|
}
|
||||||
|
}
|
78
veilid-core/src/storage_manager/signed_value_descriptor.rs
Normal file
78
veilid-core/src/storage_manager/signed_value_descriptor.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use super::*;
|
||||||
|
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialOrd,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct SignedValueDescriptor {
|
||||||
|
owner: PublicKey,
|
||||||
|
schema_data: Vec<u8>,
|
||||||
|
signature: Signature,
|
||||||
|
}
|
||||||
|
impl SignedValueDescriptor {
|
||||||
|
pub fn new(
|
||||||
|
owner: PublicKey,
|
||||||
|
schema_data: Vec<u8>,
|
||||||
|
signature: Signature,
|
||||||
|
vcrypto: CryptoSystemVersion,
|
||||||
|
) -> Result<Self, VeilidAPIError> {
|
||||||
|
// validate signature
|
||||||
|
vcrypto.verify(&owner, &schema_data, &signature)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
owner,
|
||||||
|
schema_data,
|
||||||
|
signature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn owner(&self) -> &PublicKey {
|
||||||
|
&self.owner
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn schema_data(&self) -> &[u8] {
|
||||||
|
&self.schema_data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn schema(&self) -> Result<DHTSchema, VeilidAPIError> {
|
||||||
|
DHTSchema::try_from(self.schema_data.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self) -> &Signature {
|
||||||
|
&self.signature
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_signature(
|
||||||
|
owner: PublicKey,
|
||||||
|
schema_data: Vec<u8>,
|
||||||
|
vcrypto: CryptoSystemVersion,
|
||||||
|
owner_secret: SecretKey,
|
||||||
|
) -> Result<Self, VeilidAPIError> {
|
||||||
|
// create signature
|
||||||
|
let signature = vcrypto.sign(&owner, &owner_secret, &schema_data)?;
|
||||||
|
Ok(Self {
|
||||||
|
owner,
|
||||||
|
schema_data,
|
||||||
|
signature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_size(&self) -> usize {
|
||||||
|
mem::size_of::<Self>() + self.schema_data.len()
|
||||||
|
}
|
||||||
|
}
|
43
veilid-core/src/storage_manager/value_detail.rs
Normal file
43
veilid-core/src/storage_manager/value_detail.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use super::*;
|
||||||
|
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialOrd,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct ValueDetail {
|
||||||
|
signed_value_data: SignedValueData,
|
||||||
|
descriptor: Option<SignedValueDescriptor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueDetail {
|
||||||
|
pub fn new(
|
||||||
|
signed_value_data: SignedValueData,
|
||||||
|
descriptor: Option<SignedValueDescriptor>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
signed_value_data,
|
||||||
|
descriptor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn signed_value_data(&self) -> &SignedValueData {
|
||||||
|
&self.signed_value_data
|
||||||
|
}
|
||||||
|
pub fn descriptor(&self) -> Option<&SignedValueDescriptor> {
|
||||||
|
self.descriptor.as_ref()
|
||||||
|
}
|
||||||
|
}
|
@ -42,169 +42,7 @@ pub async fn test_attach_detach() {
|
|||||||
api.shutdown().await;
|
api.shutdown().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn test_signed_node_info() {
|
|
||||||
info!("--- test_signed_node_info ---");
|
|
||||||
|
|
||||||
let (update_callback, config_callback) = setup_veilid_core();
|
|
||||||
let api = api_startup(update_callback, config_callback)
|
|
||||||
.await
|
|
||||||
.expect("startup failed");
|
|
||||||
|
|
||||||
let crypto = api.crypto().unwrap();
|
|
||||||
for ck in VALID_CRYPTO_KINDS {
|
|
||||||
let vcrypto = crypto.get(ck).unwrap();
|
|
||||||
|
|
||||||
// Test direct
|
|
||||||
let node_info = NodeInfo {
|
|
||||||
network_class: NetworkClass::InboundCapable,
|
|
||||||
outbound_protocols: ProtocolTypeSet::all(),
|
|
||||||
address_types: AddressTypeSet::all(),
|
|
||||||
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
|
|
||||||
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
|
|
||||||
dial_info_detail_list: vec![DialInfoDetail {
|
|
||||||
class: DialInfoClass::Mapped,
|
|
||||||
dial_info: DialInfo::udp(SocketAddress::default()),
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test correct validation
|
|
||||||
let keypair = vcrypto.generate_keypair();
|
|
||||||
let sni = SignedDirectNodeInfo::make_signatures(
|
|
||||||
crypto.clone(),
|
|
||||||
vec![TypedKeyPair::new(ck, keypair)],
|
|
||||||
node_info.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let mut tks: TypedKeySet = TypedKey::new(ck, keypair.key).into();
|
|
||||||
let oldtkslen = tks.len();
|
|
||||||
let _ = SignedDirectNodeInfo::new(
|
|
||||||
crypto.clone(),
|
|
||||||
&mut tks,
|
|
||||||
node_info.clone(),
|
|
||||||
sni.timestamp,
|
|
||||||
sni.signatures.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(tks.len(), oldtkslen);
|
|
||||||
assert_eq!(tks.len(), sni.signatures.len());
|
|
||||||
|
|
||||||
// Test incorrect validation
|
|
||||||
let keypair1 = vcrypto.generate_keypair();
|
|
||||||
let mut tks1: TypedKeySet = TypedKey::new(ck, keypair1.key).into();
|
|
||||||
let oldtks1len = tks1.len();
|
|
||||||
let _ = SignedDirectNodeInfo::new(
|
|
||||||
crypto.clone(),
|
|
||||||
&mut tks1,
|
|
||||||
node_info.clone(),
|
|
||||||
sni.timestamp,
|
|
||||||
sni.signatures.clone(),
|
|
||||||
)
|
|
||||||
.unwrap_err();
|
|
||||||
assert_eq!(tks1.len(), oldtks1len);
|
|
||||||
assert_eq!(tks1.len(), sni.signatures.len());
|
|
||||||
|
|
||||||
// Test unsupported cryptosystem validation
|
|
||||||
let fake_crypto_kind: CryptoKind = FourCC::from([0, 1, 2, 3]);
|
|
||||||
let mut tksfake: TypedKeySet = TypedKey::new(fake_crypto_kind, PublicKey::default()).into();
|
|
||||||
let mut sigsfake = sni.signatures.clone();
|
|
||||||
sigsfake.push(TypedSignature::new(fake_crypto_kind, Signature::default()));
|
|
||||||
tksfake.add(TypedKey::new(ck, keypair.key));
|
|
||||||
let sdnifake = SignedDirectNodeInfo::new(
|
|
||||||
crypto.clone(),
|
|
||||||
&mut tksfake,
|
|
||||||
node_info.clone(),
|
|
||||||
sni.timestamp,
|
|
||||||
sigsfake.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(tksfake.len(), 1);
|
|
||||||
assert_eq!(sdnifake.signatures.len(), sigsfake.len());
|
|
||||||
|
|
||||||
// Test relayed
|
|
||||||
let node_info2 = NodeInfo {
|
|
||||||
network_class: NetworkClass::OutboundOnly,
|
|
||||||
outbound_protocols: ProtocolTypeSet::all(),
|
|
||||||
address_types: AddressTypeSet::all(),
|
|
||||||
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
|
|
||||||
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
|
|
||||||
dial_info_detail_list: vec![DialInfoDetail {
|
|
||||||
class: DialInfoClass::Blocked,
|
|
||||||
dial_info: DialInfo::udp(SocketAddress::default()),
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test correct validation
|
|
||||||
let keypair2 = vcrypto.generate_keypair();
|
|
||||||
let mut tks2: TypedKeySet = TypedKey::new(ck, keypair2.key).into();
|
|
||||||
let oldtks2len = tks2.len();
|
|
||||||
|
|
||||||
let sni2 = SignedRelayedNodeInfo::make_signatures(
|
|
||||||
crypto.clone(),
|
|
||||||
vec![TypedKeyPair::new(ck, keypair2)],
|
|
||||||
node_info2.clone(),
|
|
||||||
tks.clone(),
|
|
||||||
sni.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let _ = SignedRelayedNodeInfo::new(
|
|
||||||
crypto.clone(),
|
|
||||||
&mut tks2,
|
|
||||||
node_info2.clone(),
|
|
||||||
tks.clone(),
|
|
||||||
sni.clone(),
|
|
||||||
sni2.timestamp,
|
|
||||||
sni2.signatures.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(tks2.len(), oldtks2len);
|
|
||||||
assert_eq!(tks2.len(), sni2.signatures.len());
|
|
||||||
|
|
||||||
// Test incorrect validation
|
|
||||||
let keypair3 = vcrypto.generate_keypair();
|
|
||||||
let mut tks3: TypedKeySet = TypedKey::new(ck, keypair3.key).into();
|
|
||||||
let oldtks3len = tks3.len();
|
|
||||||
|
|
||||||
let _ = SignedRelayedNodeInfo::new(
|
|
||||||
crypto.clone(),
|
|
||||||
&mut tks3,
|
|
||||||
node_info2.clone(),
|
|
||||||
tks.clone(),
|
|
||||||
sni.clone(),
|
|
||||||
sni2.timestamp,
|
|
||||||
sni2.signatures.clone(),
|
|
||||||
)
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(tks3.len(), oldtks3len);
|
|
||||||
assert_eq!(tks3.len(), sni2.signatures.len());
|
|
||||||
|
|
||||||
// Test unsupported cryptosystem validation
|
|
||||||
let fake_crypto_kind: CryptoKind = FourCC::from([0, 1, 2, 3]);
|
|
||||||
let mut tksfake3: TypedKeySet =
|
|
||||||
TypedKey::new(fake_crypto_kind, PublicKey::default()).into();
|
|
||||||
let mut sigsfake3 = sni2.signatures.clone();
|
|
||||||
sigsfake3.push(TypedSignature::new(fake_crypto_kind, Signature::default()));
|
|
||||||
tksfake3.add(TypedKey::new(ck, keypair2.key));
|
|
||||||
let srnifake = SignedRelayedNodeInfo::new(
|
|
||||||
crypto.clone(),
|
|
||||||
&mut tksfake3,
|
|
||||||
node_info2.clone(),
|
|
||||||
tks.clone(),
|
|
||||||
sni.clone(),
|
|
||||||
sni2.timestamp,
|
|
||||||
sigsfake3.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(tksfake3.len(), 1);
|
|
||||||
assert_eq!(srnifake.signatures.len(), sigsfake3.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
api.shutdown().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn test_all() {
|
pub async fn test_all() {
|
||||||
test_startup_shutdown().await;
|
test_startup_shutdown().await;
|
||||||
test_attach_detach().await;
|
test_attach_detach().await;
|
||||||
test_signed_node_info().await;
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ pub async fn run_all_tests() {
|
|||||||
test_veilid_config::test_all().await;
|
test_veilid_config::test_all().await;
|
||||||
info!("TEST: test_connection_table");
|
info!("TEST: test_connection_table");
|
||||||
test_connection_table::test_all().await;
|
test_connection_table::test_all().await;
|
||||||
|
info!("TEST: test_signed_node_info");
|
||||||
|
test_signed_node_info::test_all().await;
|
||||||
info!("TEST: test_table_store");
|
info!("TEST: test_table_store");
|
||||||
test_table_store::test_all().await;
|
test_table_store::test_all().await;
|
||||||
info!("TEST: test_protected_store");
|
info!("TEST: test_protected_store");
|
||||||
@ -116,6 +118,15 @@ cfg_if! {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn run_test_signed_node_info() {
|
||||||
|
setup();
|
||||||
|
block_on(async {
|
||||||
|
test_signed_node_info::test_all().await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn run_test_table_store() {
|
fn run_test_table_store() {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use data_encoding::BASE64URL_NOPAD;
|
use data_encoding::BASE64URL_NOPAD;
|
||||||
|
use network_manager::*;
|
||||||
use routing_table::*;
|
use routing_table::*;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
mod aligned_u64;
|
|
||||||
mod api;
|
mod api;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod error;
|
mod error;
|
||||||
@ -8,7 +7,6 @@ mod routing_context;
|
|||||||
mod serialize_helpers;
|
mod serialize_helpers;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use aligned_u64::*;
|
|
||||||
pub use api::*;
|
pub use api::*;
|
||||||
pub use debug::*;
|
pub use debug::*;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
@ -31,7 +29,7 @@ use core::fmt;
|
|||||||
use core_context::{api_shutdown, VeilidCoreContext};
|
use core_context::{api_shutdown, VeilidCoreContext};
|
||||||
use enumset::*;
|
use enumset::*;
|
||||||
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||||
use routing_table::{RouteSpecStore, RoutingTable};
|
use routing_table::{Direction, RouteSpecStore, RoutingTable};
|
||||||
use rpc_processor::*;
|
use rpc_processor::*;
|
||||||
use serde::*;
|
use serde::*;
|
||||||
use storage_manager::StorageManager;
|
use storage_manager::StorageManager;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -120,3 +120,17 @@ impl AlignedU64 {
|
|||||||
Self(self.0.saturating_sub(rhs.0))
|
Self(self.0.saturating_sub(rhs.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// Microseconds since epoch
|
||||||
|
pub type Timestamp = AlignedU64;
|
||||||
|
pub fn get_aligned_timestamp() -> Timestamp {
|
||||||
|
get_timestamp().into()
|
||||||
|
}
|
||||||
|
/// Microseconds duration
|
||||||
|
pub type TimestampDuration = AlignedU64;
|
||||||
|
/// Request/Response matching id
|
||||||
|
pub type OperationId = AlignedU64;
|
||||||
|
/// Number of bytes
|
||||||
|
pub type ByteCount = AlignedU64;
|
32
veilid-core/src/veilid_api/types/app_message_call.rs
Normal file
32
veilid-core/src/veilid_api/types/app_message_call.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Direct statement blob passed to hosting application for processing
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidAppMessage {
|
||||||
|
/// Some(sender) if the message was sent directly, None if received via a private/safety route
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
|
pub sender: Option<PublicKey>,
|
||||||
|
/// The content of the message to deliver to the application
|
||||||
|
#[serde(with = "json_as_base64")]
|
||||||
|
pub message: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Direct question blob passed to hosting application for processing to send an eventual AppReply
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidAppCall {
|
||||||
|
/// Some(sender) if the request was sent directly, None if received via a private/safety route
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
|
pub sender: Option<PublicKey>,
|
||||||
|
/// The content of the request to deliver to the application
|
||||||
|
#[serde(with = "json_as_base64")]
|
||||||
|
pub message: Vec<u8>,
|
||||||
|
/// The id to reply to
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub id: OperationId,
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// DHT Record Descriptor
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DHTRecordDescriptor {
|
||||||
|
owner: PublicKey,
|
||||||
|
schema: DHTSchema,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DHTRecordDescriptor {
|
||||||
|
pub fn new(owner: PublicKey, schema: DHTSchema) -> Self {
|
||||||
|
Self { owner, schema }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn owner(&self) -> PublicKey {
|
||||||
|
self.owner
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn schema(&self) -> DHTSchema {
|
||||||
|
self.schema
|
||||||
|
}
|
||||||
|
}
|
16
veilid-core/src/veilid_api/types/dht/mod.rs
Normal file
16
veilid-core/src/veilid_api/types/dht/mod.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
mod dht_record_descriptor;
|
||||||
|
mod schema;
|
||||||
|
mod value_data;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use dht_record_descriptor::*;
|
||||||
|
pub use schema::*;
|
||||||
|
pub use value_data::*;
|
||||||
|
|
||||||
|
/// Value subkey
|
||||||
|
pub type ValueSubkey = u32;
|
||||||
|
/// Value subkey range
|
||||||
|
pub type ValueSubkeyRange = (u32, u32);
|
||||||
|
/// Value sequence number
|
||||||
|
pub type ValueSeqNum = u32;
|
61
veilid-core/src/veilid_api/types/dht/schema/dflt.rs
Normal file
61
veilid-core/src/veilid_api/types/dht/schema/dflt.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Default DHT Schema (DFLT)
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DHTSchemaDFLT {
|
||||||
|
/// Owner subkey count
|
||||||
|
pub o_cnt: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DHTSchemaDFLT {
|
||||||
|
pub const FCC: [u8; 4] = *b"DFLT";
|
||||||
|
pub const FIXED_SIZE: usize = 6;
|
||||||
|
|
||||||
|
/// Build the data representation of the schema
|
||||||
|
pub fn compile(&self) -> Vec<u8> {
|
||||||
|
let mut out = Vec::<u8>::with_capacity(Self::FIXED_SIZE);
|
||||||
|
// kind
|
||||||
|
out.extend_from_slice(&Self::FCC);
|
||||||
|
// o_cnt
|
||||||
|
out.extend_from_slice(&self.o_cnt.to_le_bytes());
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of subkeys this schema allocates
|
||||||
|
pub fn subkey_count(&self) -> usize {
|
||||||
|
self.o_cnt as usize
|
||||||
|
}
|
||||||
|
/// Get the data size of this schema beyond the size of the structure itself
|
||||||
|
pub fn data_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for DHTSchemaDFLT {
|
||||||
|
type Error = VeilidAPIError;
|
||||||
|
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
if b.len() != Self::FIXED_SIZE {
|
||||||
|
apibail_generic!("invalid size");
|
||||||
|
}
|
||||||
|
if &b[0..4] != &Self::FCC {
|
||||||
|
apibail_generic!("wrong fourcc");
|
||||||
|
}
|
||||||
|
|
||||||
|
let o_cnt = u16::from_le_bytes(b[4..6].try_into().map_err(VeilidAPIError::internal)?);
|
||||||
|
|
||||||
|
Ok(Self { o_cnt })
|
||||||
|
}
|
||||||
|
}
|
84
veilid-core/src/veilid_api/types/dht/schema/mod.rs
Normal file
84
veilid-core/src/veilid_api/types/dht/schema/mod.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
mod dflt;
|
||||||
|
mod smpl;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use dflt::*;
|
||||||
|
pub use smpl::*;
|
||||||
|
|
||||||
|
/// Enum over all the supported DHT Schemas
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
|
pub enum DHTSchema {
|
||||||
|
DFLT(DHTSchemaDFLT),
|
||||||
|
SMPL(DHTSchemaSMPL),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DHTSchema {
|
||||||
|
pub fn dflt(o_cnt: u16) -> DHTSchema {
|
||||||
|
DHTSchema::DFLT(DHTSchemaDFLT { o_cnt })
|
||||||
|
}
|
||||||
|
pub fn smpl(o_cnt: u16, members: Vec<DHTSchemaSMPLMember>) -> DHTSchema {
|
||||||
|
DHTSchema::SMPL(DHTSchemaSMPL { o_cnt, members })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the data representation of the schema
|
||||||
|
pub fn compile(&self) -> Vec<u8> {
|
||||||
|
match self {
|
||||||
|
DHTSchema::DFLT(d) => d.compile(),
|
||||||
|
DHTSchema::SMPL(s) => s.compile(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of subkeys this schema allocates
|
||||||
|
pub fn subkey_count(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
DHTSchema::DFLT(d) => d.subkey_count(),
|
||||||
|
DHTSchema::SMPL(s) => s.subkey_count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the data size of this schema beyond the size of the structure itself
|
||||||
|
pub fn data_size(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
DHTSchema::DFLT(d) => d.data_size(),
|
||||||
|
DHTSchema::SMPL(s) => s.data_size(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DHTSchema {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::dflt(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for DHTSchema {
|
||||||
|
type Error = VeilidAPIError;
|
||||||
|
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
if b.len() < 4 {
|
||||||
|
apibail_generic!("invalid size");
|
||||||
|
}
|
||||||
|
let fcc: [u8; 4] = b[0..4].try_into().unwrap();
|
||||||
|
match fcc {
|
||||||
|
DHTSchemaDFLT::FCC => Ok(DHTSchema::DFLT(DHTSchemaDFLT::try_from(b)?)),
|
||||||
|
DHTSchemaSMPL::FCC => Ok(DHTSchema::SMPL(DHTSchemaSMPL::try_from(b)?)),
|
||||||
|
_ => {
|
||||||
|
apibail_generic!("unknown fourcc");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
veilid-core/src/veilid_api/types/dht/schema/smpl.rs
Normal file
114
veilid-core/src/veilid_api/types/dht/schema/smpl.rs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Simple DHT Schema (SMPL) Member
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DHTSchemaSMPLMember {
|
||||||
|
/// Member key
|
||||||
|
pub m_key: PublicKey,
|
||||||
|
/// Member subkey countanyway,
|
||||||
|
pub m_cnt: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple DHT Schema (SMPL)
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct DHTSchemaSMPL {
|
||||||
|
/// Owner subkey count
|
||||||
|
pub o_cnt: u16,
|
||||||
|
/// Members
|
||||||
|
pub members: Vec<DHTSchemaSMPLMember>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DHTSchemaSMPL {
|
||||||
|
pub const FCC: [u8; 4] = *b"SMPL";
|
||||||
|
pub const FIXED_SIZE: usize = 6;
|
||||||
|
|
||||||
|
/// Build the data representation of the schema
|
||||||
|
pub fn compile(&self) -> Vec<u8> {
|
||||||
|
let mut out = Vec::<u8>::with_capacity(
|
||||||
|
Self::FIXED_SIZE + (self.members.len() * (PUBLIC_KEY_LENGTH + 2)),
|
||||||
|
);
|
||||||
|
// kind
|
||||||
|
out.extend_from_slice(&Self::FCC);
|
||||||
|
// o_cnt
|
||||||
|
out.extend_from_slice(&self.o_cnt.to_le_bytes());
|
||||||
|
// members
|
||||||
|
for m in self.members {
|
||||||
|
// m_key
|
||||||
|
out.extend_from_slice(&m.m_key.bytes);
|
||||||
|
// m_cnt
|
||||||
|
out.extend_from_slice(&m.m_cnt.to_le_bytes());
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of subkeys this schema allocates
|
||||||
|
pub fn subkey_count(&self) -> usize {
|
||||||
|
self.members
|
||||||
|
.iter()
|
||||||
|
.fold(self.o_cnt as usize, |acc, x| acc + (x.m_cnt as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the data size of this schema beyond the size of the structure itself
|
||||||
|
pub fn data_size(&self) -> usize {
|
||||||
|
self.members.len() * mem::size_of::<DHTSchemaSMPLMember>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for DHTSchemaSMPL {
|
||||||
|
type Error = VeilidAPIError;
|
||||||
|
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
if b.len() != Self::FIXED_SIZE {
|
||||||
|
apibail_generic!("invalid size");
|
||||||
|
}
|
||||||
|
if &b[0..4] != &Self::FCC {
|
||||||
|
apibail_generic!("wrong fourcc");
|
||||||
|
}
|
||||||
|
if (b.len() - Self::FIXED_SIZE) % (PUBLIC_KEY_LENGTH + 2) != 0 {
|
||||||
|
apibail_generic!("invalid member length");
|
||||||
|
}
|
||||||
|
|
||||||
|
let o_cnt = u16::from_le_bytes(b[4..6].try_into().map_err(VeilidAPIError::internal)?);
|
||||||
|
|
||||||
|
let members_len = (b.len() - Self::FIXED_SIZE) / (PUBLIC_KEY_LENGTH + 2);
|
||||||
|
let mut members: Vec<DHTSchemaSMPLMember> = Vec::with_capacity(members_len);
|
||||||
|
for n in 0..members_len {
|
||||||
|
let mstart = Self::FIXED_SIZE + n * (PUBLIC_KEY_LENGTH + 2);
|
||||||
|
let m_key = PublicKey::try_from(&b[mstart..mstart + PUBLIC_KEY_LENGTH])
|
||||||
|
.map_err(VeilidAPIError::internal)?;
|
||||||
|
let m_cnt = u16::from_le_bytes(
|
||||||
|
b[mstart + PUBLIC_KEY_LENGTH..mstart + PUBLIC_KEY_LENGTH + 2]
|
||||||
|
.try_into()
|
||||||
|
.map_err(VeilidAPIError::internal)?,
|
||||||
|
);
|
||||||
|
members.push(DHTSchemaSMPLMember { m_key, m_cnt });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { o_cnt, members })
|
||||||
|
}
|
||||||
|
}
|
64
veilid-core/src/veilid_api/types/dht/value_data.rs
Normal file
64
veilid-core/src/veilid_api/types/dht/value_data.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
PartialOrd,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct ValueData {
|
||||||
|
seq: ValueSeqNum,
|
||||||
|
data: Vec<u8>,
|
||||||
|
writer: PublicKey,
|
||||||
|
}
|
||||||
|
impl ValueData {
|
||||||
|
pub const MAX_LEN: usize = 32768;
|
||||||
|
|
||||||
|
pub fn new(data: Vec<u8>, writer: PublicKey) -> Self {
|
||||||
|
assert!(data.len() <= Self::MAX_LEN);
|
||||||
|
Self {
|
||||||
|
seq: 0,
|
||||||
|
data,
|
||||||
|
writer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new_with_seq(seq: ValueSeqNum, data: Vec<u8>, writer: PublicKey) -> Self {
|
||||||
|
assert!(data.len() <= Self::MAX_LEN);
|
||||||
|
Self { seq, data, writer }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seq(&self) -> ValueSeqNum {
|
||||||
|
self.seq
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writer(&self) -> &PublicKey {
|
||||||
|
&self.writer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &[u8] {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_data_mut<F, R>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Vec<u8>) -> R,
|
||||||
|
{
|
||||||
|
let out = f(&mut self.data);
|
||||||
|
assert!(self.data.len() <= Self::MAX_LEN);
|
||||||
|
self.seq += 1;
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_size(&self) -> usize {
|
||||||
|
mem::size_of::<Self>() + self.data.len()
|
||||||
|
}
|
||||||
|
}
|
52
veilid-core/src/veilid_api/types/fourcc.rs
Normal file
52
veilid-core/src/veilid_api/types/fourcc.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// FOURCC code
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes, PartialOrd, Ord, PartialEq, Eq, Hash))]
|
||||||
|
pub struct FourCC(pub [u8; 4]);
|
||||||
|
|
||||||
|
impl From<[u8; 4]> for FourCC {
|
||||||
|
fn from(b: [u8; 4]) -> Self {
|
||||||
|
Self(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl TryFrom<&[u8]> for FourCC {
|
||||||
|
type Error = VeilidAPIError;
|
||||||
|
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self(b.try_into().map_err(VeilidAPIError::generic)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FourCC {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", String::from_utf8_lossy(&self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Debug for FourCC {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{}", String::from_utf8_lossy(&self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for FourCC {
|
||||||
|
type Err = VeilidAPIError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(
|
||||||
|
s.as_bytes().try_into().map_err(VeilidAPIError::generic)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
21
veilid-core/src/veilid_api/types/mod.rs
Normal file
21
veilid-core/src/veilid_api/types/mod.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
mod aligned_u64;
|
||||||
|
mod app_message_call;
|
||||||
|
mod dht;
|
||||||
|
mod fourcc;
|
||||||
|
mod safety;
|
||||||
|
mod stats;
|
||||||
|
mod tunnel;
|
||||||
|
mod veilid_log;
|
||||||
|
mod veilid_state;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use aligned_u64::*;
|
||||||
|
pub use app_message_call::*;
|
||||||
|
pub use dht::*;
|
||||||
|
pub use fourcc::*;
|
||||||
|
pub use safety::*;
|
||||||
|
pub use stats::*;
|
||||||
|
pub use tunnel::*;
|
||||||
|
pub use veilid_log::*;
|
||||||
|
pub use veilid_state::*;
|
125
veilid-core/src/veilid_api/types/safety.rs
Normal file
125
veilid-core/src/veilid_api/types/safety.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Ordering here matters, >= is used to check strength of sequencing requirement
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum Sequencing {
|
||||||
|
NoPreference,
|
||||||
|
PreferOrdered,
|
||||||
|
EnsureOrdered,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Sequencing {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::NoPreference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordering here matters, >= is used to check strength of stability requirement
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum Stability {
|
||||||
|
LowLatency,
|
||||||
|
Reliable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Stability {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::LowLatency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The choice of safety route to include in compiled routes
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum SafetySelection {
|
||||||
|
/// Don't use a safety route, only specify the sequencing preference
|
||||||
|
Unsafe(Sequencing),
|
||||||
|
/// Use a safety route and parameters specified by a SafetySpec
|
||||||
|
Safe(SafetySpec),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SafetySelection {
|
||||||
|
pub fn get_sequencing(&self) -> Sequencing {
|
||||||
|
match self {
|
||||||
|
SafetySelection::Unsafe(seq) => *seq,
|
||||||
|
SafetySelection::Safe(ss) => ss.sequencing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SafetySelection {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Unsafe(Sequencing::NoPreference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for safety routes (sender privacy)
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct SafetySpec {
|
||||||
|
/// preferred safety route set id if it still exists
|
||||||
|
pub preferred_route: Option<RouteId>,
|
||||||
|
/// must be greater than 0
|
||||||
|
pub hop_count: usize,
|
||||||
|
/// prefer reliability over speed
|
||||||
|
pub stability: Stability,
|
||||||
|
/// prefer connection-oriented sequenced protocols
|
||||||
|
pub sequencing: Sequencing,
|
||||||
|
}
|
113
veilid-core/src/veilid_api/types/stats.rs
Normal file
113
veilid-core/src/veilid_api/types/stats.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct LatencyStats {
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub fastest: TimestampDuration, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub average: TimestampDuration, // average latency over the ROLLING_LATENCIES_SIZE last latencies
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub slowest: TimestampDuration, // slowest latency in the ROLLING_LATENCIES_SIZE last latencies
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct TransferStats {
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub total: ByteCount, // total amount transferred ever
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub maximum: ByteCount, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub average: ByteCount, // average rate over the ROLLING_TRANSFERS_SIZE last amounts
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub minimum: ByteCount, // minimum rate over the ROLLING_TRANSFERS_SIZE last amounts
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct TransferStatsDownUp {
|
||||||
|
pub down: TransferStats,
|
||||||
|
pub up: TransferStats,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct RPCStats {
|
||||||
|
pub messages_sent: u32, // number of rpcs that have been sent in the total_time range
|
||||||
|
pub messages_rcvd: u32, // number of rpcs that have been received in the total_time range
|
||||||
|
pub questions_in_flight: u32, // number of questions issued that have yet to be answered
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
|
pub last_question_ts: Option<Timestamp>, // when the peer was last questioned (either successfully or not) and we wanted an answer
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
|
pub last_seen_ts: Option<Timestamp>, // when the peer was last seen for any reason, including when we first attempted to reach out to it
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
|
pub first_consecutive_seen_ts: Option<Timestamp>, // the timestamp of the first consecutive proof-of-life for this node (an answer or received question)
|
||||||
|
pub recent_lost_answers: u32, // number of answers that have been lost since we lost reliability
|
||||||
|
pub failed_to_send: u32, // number of messages that have failed to send since we last successfully sent one
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct PeerStats {
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub time_added: Timestamp, // when the peer was added to the routing table
|
||||||
|
pub rpc_stats: RPCStats, // information about RPCs
|
||||||
|
pub latency: Option<LatencyStats>, // latencies for communications with the peer
|
||||||
|
pub transfer: TransferStatsDownUp, // Stats for communications with the peer
|
||||||
|
}
|
83
veilid-core/src/veilid_api/types/tunnel.rs
Normal file
83
veilid-core/src/veilid_api/types/tunnel.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Tunnel identifier
|
||||||
|
pub type TunnelId = AlignedU64;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialOrd,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum TunnelMode {
|
||||||
|
Raw,
|
||||||
|
Turn,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialOrd,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum TunnelError {
|
||||||
|
BadId, // Tunnel ID was rejected
|
||||||
|
NoEndpoint, // Endpoint was unreachable
|
||||||
|
RejectedMode, // Endpoint couldn't provide mode
|
||||||
|
NoCapacity, // Endpoint is full
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct TunnelEndpoint {
|
||||||
|
pub mode: TunnelMode,
|
||||||
|
pub description: String, // XXX: TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TunnelEndpoint {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
mode: TunnelMode::Raw,
|
||||||
|
description: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct FullTunnel {
|
||||||
|
pub id: TunnelId,
|
||||||
|
pub timeout: TimestampDuration,
|
||||||
|
pub local: TunnelEndpoint,
|
||||||
|
pub remote: TunnelEndpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct PartialTunnel {
|
||||||
|
pub id: TunnelId,
|
||||||
|
pub timeout: TimestampDuration,
|
||||||
|
pub local: TunnelEndpoint,
|
||||||
|
}
|
88
veilid-core/src/veilid_api/types/veilid_log.rs
Normal file
88
veilid-core/src/veilid_api/types/veilid_log.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Log level for VeilidCore
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Copy,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum VeilidLogLevel {
|
||||||
|
Error = 1,
|
||||||
|
Warn,
|
||||||
|
Info,
|
||||||
|
Debug,
|
||||||
|
Trace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VeilidLogLevel {
|
||||||
|
pub fn from_tracing_level(level: tracing::Level) -> VeilidLogLevel {
|
||||||
|
match level {
|
||||||
|
tracing::Level::ERROR => VeilidLogLevel::Error,
|
||||||
|
tracing::Level::WARN => VeilidLogLevel::Warn,
|
||||||
|
tracing::Level::INFO => VeilidLogLevel::Info,
|
||||||
|
tracing::Level::DEBUG => VeilidLogLevel::Debug,
|
||||||
|
tracing::Level::TRACE => VeilidLogLevel::Trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_log_level(level: log::Level) -> VeilidLogLevel {
|
||||||
|
match level {
|
||||||
|
log::Level::Error => VeilidLogLevel::Error,
|
||||||
|
log::Level::Warn => VeilidLogLevel::Warn,
|
||||||
|
log::Level::Info => VeilidLogLevel::Info,
|
||||||
|
log::Level::Debug => VeilidLogLevel::Debug,
|
||||||
|
log::Level::Trace => VeilidLogLevel::Trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_tracing_level(&self) -> tracing::Level {
|
||||||
|
match self {
|
||||||
|
Self::Error => tracing::Level::ERROR,
|
||||||
|
Self::Warn => tracing::Level::WARN,
|
||||||
|
Self::Info => tracing::Level::INFO,
|
||||||
|
Self::Debug => tracing::Level::DEBUG,
|
||||||
|
Self::Trace => tracing::Level::TRACE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_log_level(&self) -> log::Level {
|
||||||
|
match self {
|
||||||
|
Self::Error => log::Level::Error,
|
||||||
|
Self::Warn => log::Level::Warn,
|
||||||
|
Self::Info => log::Level::Info,
|
||||||
|
Self::Debug => log::Level::Debug,
|
||||||
|
Self::Trace => log::Level::Trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VeilidLogLevel {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
let text = match self {
|
||||||
|
Self::Error => "ERROR",
|
||||||
|
Self::Warn => "WARN",
|
||||||
|
Self::Info => "INFO",
|
||||||
|
Self::Debug => "DEBUG",
|
||||||
|
Self::Trace => "TRACE",
|
||||||
|
};
|
||||||
|
write!(f, "{}", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A VeilidCore log message with optional backtrace
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidLog {
|
||||||
|
pub log_level: VeilidLogLevel,
|
||||||
|
pub message: String,
|
||||||
|
pub backtrace: Option<String>,
|
||||||
|
}
|
144
veilid-core/src/veilid_api/types/veilid_state.rs
Normal file
144
veilid-core/src/veilid_api/types/veilid_state.rs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Attachment abstraction for network 'signal strength'
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RkyvArchive,
|
||||||
|
RkyvSerialize,
|
||||||
|
RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
pub enum AttachmentState {
|
||||||
|
Detached,
|
||||||
|
Attaching,
|
||||||
|
AttachedWeak,
|
||||||
|
AttachedGood,
|
||||||
|
AttachedStrong,
|
||||||
|
FullyAttached,
|
||||||
|
OverAttached,
|
||||||
|
Detaching,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AttachmentState {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
let out = match self {
|
||||||
|
AttachmentState::Attaching => "attaching".to_owned(),
|
||||||
|
AttachmentState::AttachedWeak => "attached_weak".to_owned(),
|
||||||
|
AttachmentState::AttachedGood => "attached_good".to_owned(),
|
||||||
|
AttachmentState::AttachedStrong => "attached_strong".to_owned(),
|
||||||
|
AttachmentState::FullyAttached => "fully_attached".to_owned(),
|
||||||
|
AttachmentState::OverAttached => "over_attached".to_owned(),
|
||||||
|
AttachmentState::Detaching => "detaching".to_owned(),
|
||||||
|
AttachmentState::Detached => "detached".to_owned(),
|
||||||
|
};
|
||||||
|
write!(f, "{}", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for AttachmentState {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match s.as_str() {
|
||||||
|
"attaching" => AttachmentState::Attaching,
|
||||||
|
"attached_weak" => AttachmentState::AttachedWeak,
|
||||||
|
"attached_good" => AttachmentState::AttachedGood,
|
||||||
|
"attached_strong" => AttachmentState::AttachedStrong,
|
||||||
|
"fully_attached" => AttachmentState::FullyAttached,
|
||||||
|
"over_attached" => AttachmentState::OverAttached,
|
||||||
|
"detaching" => AttachmentState::Detaching,
|
||||||
|
"detached" => AttachmentState::Detached,
|
||||||
|
_ => return Err(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidStateAttachment {
|
||||||
|
pub state: AttachmentState,
|
||||||
|
pub public_internet_ready: bool,
|
||||||
|
pub local_network_ready: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct PeerTableData {
|
||||||
|
pub node_ids: Vec<String>,
|
||||||
|
pub peer_address: String,
|
||||||
|
pub peer_stats: PeerStats,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidStateNetwork {
|
||||||
|
pub started: bool,
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub bps_down: ByteCount,
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
pub bps_up: ByteCount,
|
||||||
|
pub peers: Vec<PeerTableData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidStateRoute {
|
||||||
|
pub dead_routes: Vec<RouteId>,
|
||||||
|
pub dead_remote_routes: Vec<RouteId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidStateConfig {
|
||||||
|
pub config: VeilidConfigInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||||
|
)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidValueChange {
|
||||||
|
key: TypedKey,
|
||||||
|
subkeys: Vec<ValueSubkey>,
|
||||||
|
count: u32,
|
||||||
|
value: ValueData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||||
|
#[archive_attr(repr(u8), derive(CheckBytes))]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
|
pub enum VeilidUpdate {
|
||||||
|
Log(VeilidLog),
|
||||||
|
AppMessage(VeilidAppMessage),
|
||||||
|
AppCall(VeilidAppCall),
|
||||||
|
Attachment(VeilidStateAttachment),
|
||||||
|
Network(VeilidStateNetwork),
|
||||||
|
Config(VeilidStateConfig),
|
||||||
|
Route(VeilidStateRoute),
|
||||||
|
ValueChange(VeilidValueChange),
|
||||||
|
Shutdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||||
|
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||||
|
pub struct VeilidState {
|
||||||
|
pub attachment: VeilidStateAttachment,
|
||||||
|
pub network: VeilidStateNetwork,
|
||||||
|
pub config: VeilidStateConfig,
|
||||||
|
}
|
@ -60,6 +60,12 @@ async fn run_test_connection_table() {
|
|||||||
test_connection_table::test_all().await;
|
test_connection_table::test_all().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
async fn run_test_signed_node_info() {
|
||||||
|
setup();
|
||||||
|
test_signed_node_info::test_all().await;
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
async fn exec_test_table_store() {
|
async fn exec_test_table_store() {
|
||||||
setup();
|
setup();
|
||||||
|
Loading…
Reference in New Issue
Block a user