diff --git a/Cargo.lock b/Cargo.lock index 507882b7..5ed60b92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -672,6 +672,27 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytecheck" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -3871,6 +3892,26 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -4051,6 +4092,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +dependencies = [ + "bytecheck", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -4076,6 +4126,31 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rkyv" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +dependencies = [ + "bytecheck", + "hashbrown", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "rlp" version = "0.5.2" @@ -4274,6 +4349,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "secrecy" version = "0.7.0" @@ -4371,6 +4452,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +dependencies = [ + "serde", +] + [[package]] name = "serde_cbor" version = "0.11.2" @@ -5477,6 +5567,7 @@ dependencies = [ "backtrace", "blake3", "bugsalot", + "bytecheck", "capnp", "capnpc", "cfg-if 1.0.0", @@ -5520,6 +5611,7 @@ dependencies = [ "owo-colors", "parking_lot 0.12.1", "rand 0.7.3", + "rkyv", "rtnetlink", "rusqlite", "rust-fsm", @@ -5529,7 +5621,7 @@ dependencies = [ "send_wrapper 0.6.0", "serde", "serde-big-array", - "serde_cbor", + "serde_bytes", "serde_json", "serial_test", "simplelog 0.12.0", diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index cb828dfc..905ccad1 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -59,6 +59,9 @@ rtnetlink = { version = "^0", default-features = false, optional = true } async-std-resolver = { version = "^0", optional = true } trust-dns-resolver = { version = "^0", optional = true } keyvaluedb = { path = "../external/keyvaluedb/keyvaluedb" } +serde_bytes = { version = "^0" } +rkyv = { version = "^0", default_features = false, features = ["std", "alloc", "strict", "size_64", "archive_le", "validation"] } +bytecheck = "^0" # Dependencies for native builds only # Linux, Windows, Mac, iOS, Android @@ -82,7 +85,6 @@ futures-util = { version = "^0", default-features = false, features = ["async-aw keyvaluedb-sqlite = { path = "../external/keyvaluedb/keyvaluedb-sqlite" } data-encoding = { version = "^2" } serde = { version = "^1", features = ["derive" ] } -serde_cbor = { version = "^0" } serde_json = { version = "^1" } socket2 = "^0" bugsalot = "^0" @@ -99,7 +101,6 @@ no-std-net = { path = "../external/no-std-net", features = ["serde"] } keyvaluedb-web = { path = "../external/keyvaluedb/keyvaluedb-web" } data-encoding = { version = "^2", default_features = false, features = ["alloc"] } serde = { version = "^1", default-features = false, features = ["derive", "alloc"] } -serde_cbor = { version = "^0", default-features = false, features = ["alloc"] } serde_json = { version = "^1", default-features = false, features = ["alloc"] } getrandom = { version = "^0", features = ["js"] } ws_stream_wasm = "^0" diff --git a/veilid-core/proto/veilid.capnp b/veilid-core/proto/veilid.capnp index b3c19e91..c3f00bcf 100644 --- a/veilid-core/proto/veilid.capnp +++ b/veilid-core/proto/veilid.capnp @@ -173,7 +173,7 @@ struct ValueKey { # } struct ValueData { - data @0 :Data; # value or subvalue contents in CBOR format + data @0 :Data; # value or subvalue contents seq @1 :ValueSeqNum; # sequence number of value } @@ -181,9 +181,10 @@ struct ValueData { ############################## 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 @0; # X = Invalid network class, network is not yet set up + inboundCapable @1; # I = Inbound capable without relay, may require signal + outboundOnly @2; # O = Outbound only, inbound relay required except with reverse connect signal + webApp @3; # W = PWA, outbound relay is required in most cases } enum DialInfoClass { @@ -232,6 +233,10 @@ struct AddressTypeSet { ipv6 @1 :Bool; } +struct SenderInfo { + socketAddress @0 :SocketAddress; # socket address that for the sending peer +} + struct NodeInfo { networkClass @0 :NetworkClass; # network class of this node outboundProtocols @1 :ProtocolTypeSet; # protocols that can go outbound @@ -239,17 +244,27 @@ struct NodeInfo { minVersion @3 :UInt8; # minimum protocol version for rpc maxVersion @4 :UInt8; # maximum protocol version for rpc dialInfoDetailList @5 :List(DialInfoDetail); # inbound dial info details for this node - relayPeerInfo @6 :PeerInfo; # (optional) relay peer info for this node +} + +struct SignedDirectNodeInfo { + nodeInfo @0 :NodeInfo; # node info + timestamp @1 :UInt64; # when signed node info was generated + signature @2 :Signature; # signature +} + +struct SignedRelayedNodeInfo { + nodeInfo @0 :NodeInfo; # node info + relayId @1 :NodeID; # node id for relay + relayInfo @2 :SignedDirectNodeInfo; # signed node info for relay + timestamp @3 :UInt64; # when signed node info was generated + signature @4 :Signature; # signature } struct SignedNodeInfo { - nodeInfo @0 :NodeInfo; # node info - signature @1 :Signature; # signature - timestamp @2 :UInt64; # when signed node info was generated -} - -struct SenderInfo { - socketAddress @0 :SocketAddress; # socket address that for the sending peer + union { + direct @0 :SignedDirectNodeInfo; # node info for nodes reachable without a relay + relayed @1 :SignedRelayedNodeInfo; # node info for nodes requiring a relay + } } struct PeerInfo { @@ -326,7 +341,7 @@ struct OperationGetValueA { struct OperationSetValueQ { key @0 :ValueKey; # key for value to update - value @1 :ValueData; # value or subvalue contents in CBOR format (older or equal seq number gets dropped) + value @1 :ValueData; # value or subvalue contents (older or equal seq number gets dropped) } struct OperationSetValueA { @@ -347,7 +362,7 @@ struct OperationWatchValueA { struct OperationValueChanged { key @0 :ValueKey; # key for value that changed - value @1 :ValueData; # value or subvalue contents in CBOR format with sequence number + value @1 :ValueData; # value or subvalue contents with sequence number } struct OperationSupplyBlockQ { diff --git a/veilid-core/src/attachment_manager.rs b/veilid-core/src/attachment_manager.rs index 8c304e44..8afafcdd 100644 --- a/veilid-core/src/attachment_manager.rs +++ b/veilid-core/src/attachment_manager.rs @@ -9,7 +9,7 @@ use core::fmt; use serde::*; state_machine! { - derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize) + derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,) pub Attachment(Detached) //--- Detached(AttachRequested) => Attaching [StartAttachment], diff --git a/veilid-core/src/crypto/key.rs b/veilid-core/src/crypto/key.rs index c0c1934f..79ae26e5 100644 --- a/veilid-core/src/crypto/key.rs +++ b/veilid-core/src/crypto/key.rs @@ -12,7 +12,6 @@ use digest::generic_array::typenum::U64; use digest::{Digest, Output}; use ed25519_dalek::{Keypair, PublicKey, Signature}; use generic_array::GenericArray; -use serde::{Deserialize, Serialize}; ////////////////////////////////////////////////////////////////////// @@ -39,13 +38,14 @@ pub const DHT_SIGNATURE_LENGTH_ENCODED: usize = 86; macro_rules! byte_array_type { ($name:ident, $size:expr) => { - #[derive(Clone, Copy)] + #[derive(Clone, Copy, RkyvArchive, RkyvSerialize, RkyvDeserialize)] + #[archive_attr(repr(C), derive(CheckBytes))] pub struct $name { pub bytes: [u8; $size], pub valid: bool, } - impl Serialize for $name { + impl serde::Serialize for $name { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -56,16 +56,16 @@ macro_rules! byte_array_type { } else { s = "".to_owned(); } - s.serialize(serializer) + serde::Serialize::serialize(&s, serializer) } } - impl<'de> Deserialize<'de> for $name { + impl<'de> serde::Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - let s = String::deserialize(deserializer)?; + let s = ::deserialize(deserializer)?; if s == "" { return Ok($name::default()); } diff --git a/veilid-core/src/intf/native/protected_store.rs b/veilid-core/src/intf/native/protected_store.rs index 3ab88a12..a784189e 100644 --- a/veilid-core/src/intf/native/protected_store.rs +++ b/veilid-core/src/intf/native/protected_store.rs @@ -2,7 +2,6 @@ use crate::xx::*; use crate::*; use data_encoding::BASE64URL_NOPAD; use keyring_manager::*; -use serde::{Deserialize, Serialize}; use std::path::Path; pub struct ProtectedStoreInner { @@ -144,18 +143,31 @@ impl ProtectedStore { } #[instrument(level = "trace", skip(self, value))] - pub async fn save_user_secret_cbor(&self, key: &str, value: &T) -> EyreResult + pub async fn save_user_secret_rkyv(&self, key: &str, value: &T) -> EyreResult where - T: Serialize, + T: RkyvSerialize>, { - let v = serde_cbor::to_vec(value).wrap_err("couldn't store as CBOR")?; + let v = to_rkyv(value)?; + self.save_user_secret(&key, &v).await + } + + #[instrument(level = "trace", skip(self, value))] + pub async fn save_user_secret_json(&self, key: &str, value: &T) -> EyreResult + where + T: serde::Serialize, + { + let v = serde_json::to_vec(value)?; self.save_user_secret(&key, &v).await } #[instrument(level = "trace", skip(self))] - pub async fn load_user_secret_cbor(&self, key: &str) -> EyreResult> + pub async fn load_user_secret_rkyv(&self, key: &str) -> EyreResult> where - T: for<'de> Deserialize<'de>, + T: RkyvArchive, + ::Archived: + for<'t> bytecheck::CheckBytes>, + ::Archived: + rkyv::Deserialize, { let out = self.load_user_secret(key).await?; let b = match out { @@ -165,7 +177,24 @@ impl ProtectedStore { } }; - let obj = serde_cbor::from_slice::(&b).wrap_err("failed to deserialize")?; + let obj = from_rkyv(b)?; + Ok(Some(obj)) + } + + #[instrument(level = "trace", skip(self))] + pub async fn load_user_secret_json(&self, key: &str) -> EyreResult> + where + T: for<'de> serde::de::Deserialize<'de>, + { + let out = self.load_user_secret(key).await?; + let b = match out { + Some(v) => v, + None => { + return Ok(None); + } + }; + + let obj = serde_json::from_slice(&b)?; Ok(Some(obj)) } diff --git a/veilid-core/src/intf/table_db.rs b/veilid-core/src/intf/table_db.rs index 09fbe5a3..ddf6cf72 100644 --- a/veilid-core/src/intf/table_db.rs +++ b/veilid-core/src/intf/table_db.rs @@ -85,12 +85,25 @@ impl TableDB { db.write(dbt).wrap_err("failed to store key") } - /// Store a key in CBOR format with a value in a column in the TableDB. Performs a single transaction immediately. - pub fn store_cbor(&self, col: u32, key: &[u8], value: &T) -> EyreResult<()> + /// Store a key in rkyv format with a value in a column in the TableDB. Performs a single transaction immediately. + pub fn store_rkyv(&self, col: u32, key: &[u8], value: &T) -> EyreResult<()> where - T: Serialize, + T: RkyvSerialize>, { - let v = serde_cbor::to_vec(value).wrap_err("couldn't store as CBOR")?; + let v = to_rkyv(value)?; + + let db = &self.inner.lock().database; + let mut dbt = db.transaction(); + dbt.put(col, key, v.as_slice()); + db.write(dbt).wrap_err("failed to store key") + } + + /// Store a key in json format with a value in a column in the TableDB. Performs a single transaction immediately. + pub fn store_json(&self, col: u32, key: &[u8], value: &T) -> EyreResult<()> + where + T: serde::Serialize, + { + let v = serde_json::to_vec(value)?; let db = &self.inner.lock().database; let mut dbt = db.transaction(); @@ -104,10 +117,14 @@ impl TableDB { db.get(col, key).wrap_err("failed to get key") } - /// Read a key from a column in the TableDB immediately, in CBOR format. - pub fn load_cbor(&self, col: u32, key: &[u8]) -> EyreResult> + /// Read an rkyv key from a column in the TableDB immediately + pub fn load_rkyv(&self, col: u32, key: &[u8]) -> EyreResult> where - T: for<'de> Deserialize<'de>, + T: RkyvArchive, + ::Archived: + for<'t> bytecheck::CheckBytes>, + ::Archived: + rkyv::Deserialize, { let db = &self.inner.lock().database; let out = db.get(col, key).wrap_err("failed to get key")?; @@ -117,7 +134,24 @@ impl TableDB { return Ok(None); } }; - let obj = serde_cbor::from_slice::(&b).wrap_err("failed to deserialize")?; + let obj = from_rkyv(b)?; + Ok(Some(obj)) + } + + /// Read an serde-json key from a column in the TableDB immediately + pub fn load_json(&self, col: u32, key: &[u8]) -> EyreResult> + where + T: for<'de> serde::Deserialize<'de>, + { + let db = &self.inner.lock().database; + let out = db.get(col, key).wrap_err("failed to get key")?; + let b = match out { + Some(v) => v, + None => { + return Ok(None); + } + }; + let obj = serde_json::from_slice(&b)?; Ok(Some(obj)) } @@ -176,12 +210,22 @@ impl<'a> TableDBTransaction<'a> { self.dbt.as_mut().unwrap().put(col, key, value); } - /// Store a key in CBOR format with a value in a column in the TableDB - pub fn store_cbor(&mut self, col: u32, key: &[u8], value: &T) -> EyreResult<()> + /// Store a key in rkyv format with a value in a column in the TableDB + pub fn store_rkyv(&mut self, col: u32, key: &[u8], value: &T) -> EyreResult<()> + where + T: RkyvSerialize>, + { + let v = to_rkyv(value)?; + self.dbt.as_mut().unwrap().put(col, key, v.as_slice()); + Ok(()) + } + + /// Store a key in rkyv format with a value in a column in the TableDB + pub fn store_json(&mut self, col: u32, key: &[u8], value: &T) -> EyreResult<()> where T: Serialize, { - let v = serde_cbor::to_vec(value).wrap_err("couldn't store as CBOR")?; + let v = serde_json::to_vec(value)?; self.dbt.as_mut().unwrap().put(col, key, v.as_slice()); Ok(()) } diff --git a/veilid-core/src/intf/wasm/protected_store.rs b/veilid-core/src/intf/wasm/protected_store.rs index 8dd3f427..67a4d1da 100644 --- a/veilid-core/src/intf/wasm/protected_store.rs +++ b/veilid-core/src/intf/wasm/protected_store.rs @@ -136,18 +136,22 @@ impl ProtectedStore { } #[instrument(level = "trace", skip(self, value))] - pub async fn save_user_secret_cbor(&self, key: &str, value: &T) -> EyreResult + pub async fn save_user_secret_frozen(&self, key: &str, value: &T) -> EyreResult where - T: Serialize, + T: RkyvSerialize>, { - let v = serde_cbor::to_vec(value).wrap_err("couldn't store as CBOR")?; + let v = to_frozen(value)?; self.save_user_secret(&key, &v).await } #[instrument(level = "trace", skip(self))] - pub async fn load_user_secret_cbor(&self, key: &str) -> EyreResult> + pub async fn load_user_secret_frozen(&self, key: &str) -> EyreResult> where - T: for<'de> Deserialize<'de>, + T: RkyvArchive, + ::Archived: + for<'t> bytecheck::CheckBytes>, + ::Archived: + rkyv::Deserialize, { let out = self.load_user_secret(key).await?; let b = match out { @@ -157,7 +161,7 @@ impl ProtectedStore { } }; - let obj = serde_cbor::from_slice::(&b).wrap_err("failed to deserialize")?; + let obj = from_frozen(&b)?; Ok(Some(obj)) } diff --git a/veilid-core/src/network_manager/tasks.rs b/veilid-core/src/network_manager/tasks.rs index a12d6018..07554099 100644 --- a/veilid-core/src/network_manager/tasks.rs +++ b/veilid-core/src/network_manager/tasks.rs @@ -295,7 +295,7 @@ impl NetworkManager { if let Some(nr) = routing_table.register_node_with_signed_node_info( RoutingDomain::PublicInternet, k, - SignedNodeInfo::with_no_signature(NodeInfo { + SignedDirectNodeInfo::with_no_signature(NodeInfo { network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable diff --git a/veilid-core/src/routing_table/bucket.rs b/veilid-core/src/routing_table/bucket.rs index 7ffee2f7..3b17c585 100644 --- a/veilid-core/src/routing_table/bucket.rs +++ b/veilid-core/src/routing_table/bucket.rs @@ -8,7 +8,19 @@ pub struct Bucket { } pub(super) type EntriesIter<'a> = alloc::collections::btree_map::Iter<'a, DHTKey, Arc>; -type BucketData = (Vec<(DHTKey, Vec)>, Option); +#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +struct BucketEntryData { + key: DHTKey, + value: Vec, +} + +#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +struct BucketData { + entries: Vec, + newest_entry: Option, +} fn state_ordering(state: BucketEntryState) -> usize { match state { @@ -27,29 +39,33 @@ impl Bucket { } } - pub(super) fn load_bucket(&mut self, data: &[u8]) -> EyreResult<()> { - let bucket_data: BucketData = - serde_cbor::from_slice::(data).wrap_err("failed to deserialize bucket")?; + pub(super) fn load_bucket(&mut self, data: Vec) -> EyreResult<()> { + let bucket_data: BucketData = from_rkyv(data)?; - for (k, d) in bucket_data.0 { - let entryinner = serde_cbor::from_slice::(&d) - .wrap_err("failed to deserialize bucket entry")?; + for e in bucket_data.entries { + let entryinner = from_rkyv(e.value).wrap_err("failed to deserialize bucket entry")?; self.entries - .insert(k, Arc::new(BucketEntry::new_with_inner(entryinner))); + .insert(e.key, Arc::new(BucketEntry::new_with_inner(entryinner))); } - self.newest_entry = bucket_data.1; + self.newest_entry = bucket_data.newest_entry; Ok(()) } pub(super) fn save_bucket(&self) -> EyreResult> { - let mut entry_vec = Vec::new(); + let mut entries = Vec::new(); for (k, v) in &self.entries { - let entry_bytes = v.with_mut_inner(|e| serde_cbor::to_vec(e))?; - entry_vec.push((*k, entry_bytes)); + let entry_bytes = v.with_inner(|e| to_rkyv(e))?; + entries.push(BucketEntryData { + key: *k, + value: entry_bytes, + }); } - let bucket_data: BucketData = (entry_vec, self.newest_entry.clone()); - let out = serde_cbor::to_vec(&bucket_data)?; + let bucket_data = BucketData { + entries, + newest_entry: self.newest_entry.clone(), + }; + let out = to_rkyv(&bucket_data)?; Ok(out) } diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index 422e030a..fdecd02e 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -64,22 +64,44 @@ pub struct BucketEntryLocalNetwork { node_status: Option, } +/// A range of cryptography versions supported by this entry +#[derive(Debug, Serialize, Deserialize)] +pub struct VersionRange { + /// The minimum cryptography version supported by this entry + min: u8, + /// The maximum cryptography version supported by this entry + max: u8, +} + +/// The data associated with each bucket entry #[derive(Debug, Serialize, Deserialize)] pub struct BucketEntryInner { - min_max_version: Option<(u8, u8)>, + /// The minimum and maximum range of cryptography versions supported by the node, + /// inclusive of the requirements of any relay the node may be using + min_max_version: Option, + /// Whether or not we have updated this peer with our node info since our network + /// and dial info has last changed, for example when our IP address changes updated_since_last_network_change: bool, + /// The last connection descriptors used to contact this node, per protocol type #[serde(skip)] last_connections: BTreeMap, + /// The node info for this entry on the publicinternet routing domain public_internet: BucketEntryPublicInternet, + /// The node info for this entry on the localnetwork routing domain local_network: BucketEntryLocalNetwork, + /// Statistics gathered for the peer peer_stats: PeerStats, + /// The accounting for the latency statistics #[serde(skip)] latency_stats_accounting: LatencyStatsAccounting, + /// The accounting for the transfer statistics #[serde(skip)] transfer_stats_accounting: TransferStatsAccounting, + /// Tracking identifier for NodeRef debugging #[cfg(feature = "tracking")] #[serde(skip)] next_track_id: usize, + /// Backtraces for NodeRef debugging #[cfg(feature = "tracking")] #[serde(skip)] node_ref_tracks: HashMap, @@ -190,12 +212,12 @@ impl BucketEntryInner { // Always allow overwriting invalid/unsigned node if current_sni.has_valid_signature() { // If the timestamp hasn't changed or is less, ignore this update - if signed_node_info.timestamp <= current_sni.timestamp { + if signed_node_info.timestamp() <= current_sni.timestamp() { // If we received a node update with the same timestamp // we can make this node live again, but only if our network has recently changed // which may make nodes that were unreachable now reachable with the same dialinfo if !self.updated_since_last_network_change - && signed_node_info.timestamp == current_sni.timestamp + && signed_node_info.timestamp() == current_sni.timestamp() { // No need to update the signednodeinfo though since the timestamp is the same // Touch the node and let it try to live again @@ -207,11 +229,22 @@ impl BucketEntryInner { } } - // Update the protocol min/max version we have - self.min_max_version = Some(( - signed_node_info.node_info.min_version, - signed_node_info.node_info.max_version, - )); + // Update the protocol min/max version we have to use, to include relay requirements if needed + let mut version_range = VersionRange { + min: signed_node_info.node_info().min_version, + max: signed_node_info.node_info().max_version, + }; + if let Some(relay_info) = signed_node_info.relay_info() { + version_range.min.max_assign(relay_info.min_version); + version_range.max.min_assign(relay_info.max_version); + } + if version_range.min <= version_range.max { + // Can be reached with at least one crypto version + self.min_max_version = Some(version_range); + } else { + // No valid crypto version in range + self.min_max_version = None; + } // Update the signed node info *opt_current_sni = Some(Box::new(signed_node_info)); @@ -238,7 +271,7 @@ impl BucketEntryInner { RoutingDomain::LocalNetwork => &self.local_network.signed_node_info, RoutingDomain::PublicInternet => &self.public_internet.signed_node_info, }; - opt_current_sni.as_ref().map(|s| &s.node_info) + opt_current_sni.as_ref().map(|s| s.node_info()) } pub fn signed_node_info(&self, routing_domain: RoutingDomain) -> Option<&SignedNodeInfo> { @@ -338,11 +371,11 @@ impl BucketEntryInner { out } - pub fn set_min_max_version(&mut self, min_max_version: (u8, u8)) { + pub fn set_min_max_version(&mut self, min_max_version: VersionRange) { self.min_max_version = Some(min_max_version); } - pub fn min_max_version(&self) -> Option<(u8, u8)> { + pub fn min_max_version(&self) -> Option { self.min_max_version } diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 3ab5d8d5..fd1db5e2 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -163,7 +163,7 @@ impl RoutingTable { // Load bucket entries from table db if possible debug!("loading routing table entries"); if let Err(e) = self.load_buckets().await { - log_rtab!(warn "Error loading buckets from storage: {}. Resetting.", e); + log_rtab!(debug "Error loading buckets from storage: {:#?}. Resetting.", e); let mut inner = self.inner.write(); inner.init_buckets(self.clone()); } @@ -173,7 +173,7 @@ impl RoutingTable { let route_spec_store = match RouteSpecStore::load(self.clone()).await { Ok(v) => v, Err(e) => { - log_rtab!(warn "Error loading route spec store: {}. Resetting.", e); + log_rtab!(debug "Error loading route spec store: {:#?}. Resetting.", e); RouteSpecStore::new(self.clone()) } }; @@ -239,7 +239,7 @@ impl RoutingTable { let tdb = table_store.open("routing_table", 1).await?; let bucket_count = bucketvec.len(); let mut dbx = tdb.transact(); - if let Err(e) = dbx.store_cbor(0, b"bucket_count", &bucket_count) { + if let Err(e) = dbx.store_frozen(0, b"bucket_count", &bucket_count) { dbx.rollback(); return Err(e); } @@ -253,14 +253,13 @@ impl RoutingTable { async fn load_buckets(&self) -> EyreResult<()> { // Deserialize all entries - let inner = &mut *self.inner.write(); - let tstore = self.network_manager().table_store(); let tdb = tstore.open("routing_table", 1).await?; - let Some(bucket_count): Option = tdb.load_cbor(0, b"bucket_count")? else { + let Some(bucket_count): Option = tdb.load_rkyv(0, b"bucket_count")? else { log_rtab!(debug "no bucket count in saved routing table"); return Ok(()); }; + let inner = &mut *self.inner.write(); if bucket_count != inner.buckets.len() { // Must have the same number of buckets warn!("bucket count is different, not loading routing table"); @@ -275,8 +274,8 @@ impl RoutingTable { }; bucketdata_vec.push(bucketdata); } - for n in 0..bucket_count { - inner.buckets[n].load_bucket(&bucketdata_vec[n])?; + for (n, bucketdata) in bucketdata_vec.into_iter().enumerate() { + inner.buckets[n].load_bucket(bucketdata)?; } Ok(()) @@ -383,7 +382,7 @@ impl RoutingTable { } /// Return a copy of our node's signednodeinfo - pub fn get_own_signed_node_info(&self, routing_domain: RoutingDomain) -> SignedNodeInfo { + pub fn get_own_signed_node_info(&self, routing_domain: RoutingDomain) -> SignedDirectNodeInfo { self.inner.read().get_own_signed_node_info(routing_domain) } @@ -526,7 +525,7 @@ impl RoutingTable { &self, routing_domain: RoutingDomain, node_id: DHTKey, - signed_node_info: SignedNodeInfo, + signed_node_info: SignedDirectNodeInfo, allow_invalid: bool, ) -> Option { self.inner.write().register_node_with_signed_node_info( diff --git a/veilid-core/src/routing_table/node_ref.rs b/veilid-core/src/routing_table/node_ref.rs index 4b660037..6f7f9adf 100644 --- a/veilid-core/src/routing_table/node_ref.rs +++ b/veilid-core/src/routing_table/node_ref.rs @@ -385,9 +385,12 @@ impl NodeRef { out } - pub fn locked<'a>(&self, rti: &'a mut RoutingTableInner) -> NodeRefLocked<'a> { + pub fn locked<'a>(&self, rti: &'a RoutingTableInner) -> NodeRefLocked<'a> { NodeRefLocked::new(rti, self.clone()) } + pub fn locked_mut<'a>(&self, rti: &'a mut RoutingTableInner) -> NodeRefLockedMut<'a> { + NodeRefLockedMut::new(rti, self.clone()) + } } impl NodeRefBase for NodeRef { @@ -480,12 +483,12 @@ impl Drop for NodeRef { /// already locked a RoutingTableInner /// Keeps entry in the routing table until all references are gone pub struct NodeRefLocked<'a> { - inner: Mutex<&'a mut RoutingTableInner>, + inner: Mutex<&'a RoutingTableInner>, nr: NodeRef, } impl<'a> NodeRefLocked<'a> { - pub fn new(inner: &'a mut RoutingTableInner, nr: NodeRef) -> Self { + pub fn new(inner: &'a RoutingTableInner, nr: NodeRef) -> Self { Self { inner: Mutex::new(inner), nr, @@ -510,6 +513,65 @@ impl<'a> NodeRefBase for NodeRefLocked<'a> { self.nr.common.entry.with(inner, f) } + fn operate_mut(&self, _f: F) -> T + where + F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T, + { + panic!("need to locked_mut() for this operation") + } +} + +impl<'a> fmt::Display for NodeRefLocked<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.nr) + } +} + +impl<'a> fmt::Debug for NodeRefLocked<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NodeRefLocked") + .field("nr", &self.nr) + .finish() + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +/// Mutable locked reference to a routing table entry +/// For internal use inside the RoutingTable module where you have +/// already locked a RoutingTableInner +/// Keeps entry in the routing table until all references are gone +pub struct NodeRefLockedMut<'a> { + inner: Mutex<&'a mut RoutingTableInner>, + nr: NodeRef, +} + +impl<'a> NodeRefLockedMut<'a> { + pub fn new(inner: &'a mut RoutingTableInner, nr: NodeRef) -> Self { + Self { + inner: Mutex::new(inner), + nr, + } + } +} + +impl<'a> NodeRefBase for NodeRefLockedMut<'a> { + fn common(&self) -> &NodeRefBaseCommon { + &self.nr.common + } + + fn common_mut(&mut self) -> &mut NodeRefBaseCommon { + &mut self.nr.common + } + + fn operate(&self, f: F) -> T + where + F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T, + { + let inner = &*self.inner.lock(); + self.nr.common.entry.with(inner, f) + } + fn operate_mut(&self, f: F) -> T where F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T, @@ -519,15 +581,15 @@ impl<'a> NodeRefBase for NodeRefLocked<'a> { } } -impl<'a> fmt::Display for NodeRefLocked<'a> { +impl<'a> fmt::Display for NodeRefLockedMut<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.nr) } } -impl<'a> fmt::Debug for NodeRefLocked<'a> { +impl<'a> fmt::Debug for NodeRefLockedMut<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("NodeRefLocked") + f.debug_struct("NodeRefLockedMut") .field("nr", &self.nr) .finish() } diff --git a/veilid-core/src/routing_table/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store.rs index 75d4f586..b10d2b76 100644 --- a/veilid-core/src/routing_table/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store.rs @@ -221,11 +221,11 @@ impl RouteSpecStore { ) }; - // Get cbor blob from table store + // Get frozen blob from table store let table_store = routing_table.network_manager().table_store(); let rsstdb = table_store.open("RouteSpecStore", 1).await?; let mut content: RouteSpecStoreContent = - rsstdb.load_cbor(0, b"content")?.unwrap_or_default(); + rsstdb.load_json(0, b"content")?.unwrap_or_default(); // Look up all route hop noderefs since we can't serialize those let mut dead_keys = Vec::new(); @@ -246,7 +246,7 @@ impl RouteSpecStore { // Load secrets from pstore let pstore = routing_table.network_manager().protected_store(); let out: Vec<(DHTKey, DHTKeySecret)> = pstore - .load_user_secret_cbor("RouteSpecStore") + .load_user_secret_rkyv("RouteSpecStore") .await? .unwrap_or_default(); @@ -289,14 +289,14 @@ impl RouteSpecStore { inner.content.clone() }; - // Save all the fields we care about to the cbor blob in table storage + // Save all the fields we care about to the frozen blob in table storage let table_store = self .unlocked_inner .routing_table .network_manager() .table_store(); let rsstdb = table_store.open("RouteSpecStore", 1).await?; - rsstdb.store_cbor(0, b"content", &content)?; + rsstdb.store_json(0, b"content", &content)?; // // Keep secrets in protected store as well let pstore = self @@ -310,7 +310,9 @@ impl RouteSpecStore { out.push((*k, v.secret_key)); } - let _ = pstore.save_user_secret_cbor("RouteSpecStore", &out).await?; // ignore if this previously existed or not + let _ = pstore + .save_user_secret_frozen("RouteSpecStore", &out) + .await?; // ignore if this previously existed or not Ok(()) } diff --git a/veilid-core/src/routing_table/routing_domains.rs b/veilid-core/src/routing_table/routing_domains.rs index 43bd7d44..54aeec46 100644 --- a/veilid-core/src/routing_table/routing_domains.rs +++ b/veilid-core/src/routing_table/routing_domains.rs @@ -101,6 +101,45 @@ impl RoutingDomainDetailCommon { self.network_class.unwrap_or(NetworkClass::Invalid) != NetworkClass::Invalid } + fn make_peer_info(&self, rti: &RoutingTableInner) -> PeerInfo { + let node_info = NodeInfo { + network_class: self.network_class.unwrap_or(NetworkClass::Invalid), + outbound_protocols: self.outbound_protocols, + address_types: self.address_types, + min_version: MIN_CRYPTO_VERSION, + max_version: MAX_CRYPTO_VERSION, + dial_info_detail_list: self.dial_info_details.clone(), + }; + + let relay_peer_info = self + .relay_node + .as_ref() + .and_then(|rn| rn.locked(rti).make_peer_info(self.routing_domain)); + + let signed_node_info = match relay_peer_info { + Some(relay_pi) => SignedNodeInfo::Relayed( + SignedRelayedNodeInfo::with_secret( + NodeId::new(rti.unlocked_inner.node_id), + node_info, + relay_pi.node_id, + relay_pi.signed_node_info, + &rti.unlocked_inner.node_id_secret, + ) + .unwrap(), + ), + None => SignedNodeInfo::Direct( + SignedDirectNodeInfo::with_secret( + NodeId::new(rti.unlocked_inner.node_id), + node_info, + &rti.unlocked_inner.node_id_secret, + ) + .unwrap(), + ), + }; + + PeerInfo::new(NodeId::new(rti.unlocked_inner.node_id), signed_node_info) + } + pub fn with_peer_info(&self, rti: &RoutingTableInner, f: F) -> R where F: FnOnce(&PeerInfo) -> R, @@ -110,7 +149,7 @@ impl RoutingDomainDetailCommon { // Regenerate peer info let pi = PeerInfo::new( NodeId::new(rti.unlocked_inner.node_id), - SignedNodeInfo::with_secret( + SignedDirectNodeInfo::with_secret( NodeInfo { network_class: self.network_class.unwrap_or(NetworkClass::Invalid), outbound_protocols: self.outbound_protocols, @@ -118,10 +157,11 @@ impl RoutingDomainDetailCommon { min_version: MIN_CRYPTO_VERSION, max_version: MAX_CRYPTO_VERSION, dial_info_detail_list: self.dial_info_details.clone(), - relay_peer_info: self - .relay_node - .as_ref() - .and_then(|rn| rn.make_peer_info(self.routing_domain).map(Box::new)), + relay_peer_info: self.relay_node.as_ref().and_then(|rn| { + rn.locked(rti) + .make_peer_info(self.routing_domain) + .map(Box::new) + }), }, NodeId::new(rti.unlocked_inner.node_id), &rti.unlocked_inner.node_id_secret, diff --git a/veilid-core/src/routing_table/routing_table_inner.rs b/veilid-core/src/routing_table/routing_table_inner.rs index 6435cd1e..cfd162aa 100644 --- a/veilid-core/src/routing_table/routing_table_inner.rs +++ b/veilid-core/src/routing_table/routing_table_inner.rs @@ -253,7 +253,7 @@ impl RoutingTableInner { } /// Return a copy of our node's signednodeinfo - pub fn get_own_signed_node_info(&self, routing_domain: RoutingDomain) -> SignedNodeInfo { + pub fn get_own_signed_node_info(&self, routing_domain: RoutingDomain) -> SignedDirectNodeInfo { self.with_routing_domain(routing_domain, |rdd| { rdd.common() .with_peer_info(self, |pi| pi.signed_node_info.clone()) @@ -662,7 +662,7 @@ impl RoutingTableInner { outer_self: RoutingTable, routing_domain: RoutingDomain, node_id: DHTKey, - signed_node_info: SignedNodeInfo, + signed_node_info: SignedDirectNodeInfo, allow_invalid: bool, ) -> Option { // validate signed node info is not something malicious @@ -717,7 +717,8 @@ impl RoutingTableInner { }); if let Some(nr) = &out { // set the most recent node address for connection finding and udp replies - nr.locked(self).set_last_connection(descriptor, timestamp); + nr.locked_mut(self) + .set_last_connection(descriptor, timestamp); } out } diff --git a/veilid-core/src/rpc_processor/coders/mod.rs b/veilid-core/src/rpc_processor/coders/mod.rs index a22e3dfe..3211b338 100644 --- a/veilid-core/src/rpc_processor/coders/mod.rs +++ b/veilid-core/src/rpc_processor/coders/mod.rs @@ -17,7 +17,9 @@ mod public_key; mod sender_info; mod signal_info; mod signature; +mod signed_direct_node_info; mod signed_node_info; +mod signed_relayed_node_info; mod socket_address; mod tunnel; mod value_data; @@ -42,7 +44,9 @@ pub use public_key::*; pub use sender_info::*; pub use signal_info::*; pub use signature::*; +pub use signed_direct_node_info::*; pub use signed_node_info::*; +pub use signed_relayed_node_info::*; pub use socket_address::*; pub use tunnel::*; pub use value_data::*; diff --git a/veilid-core/src/rpc_processor/coders/network_class.rs b/veilid-core/src/rpc_processor/coders/network_class.rs index eaea3d78..88d17fce 100644 --- a/veilid-core/src/rpc_processor/coders/network_class.rs +++ b/veilid-core/src/rpc_processor/coders/network_class.rs @@ -5,7 +5,7 @@ pub fn encode_network_class(network_class: NetworkClass) -> veilid_capnp::Networ NetworkClass::InboundCapable => veilid_capnp::NetworkClass::InboundCapable, NetworkClass::OutboundOnly => veilid_capnp::NetworkClass::OutboundOnly, NetworkClass::WebApp => veilid_capnp::NetworkClass::WebApp, - NetworkClass::Invalid => panic!("invalid network class should not be encoded"), + NetworkClass::Invalid => veilid_capnp::NetworkClass::Invalid, } } @@ -14,5 +14,6 @@ pub fn decode_network_class(network_class: veilid_capnp::NetworkClass) -> Networ veilid_capnp::NetworkClass::InboundCapable => NetworkClass::InboundCapable, veilid_capnp::NetworkClass::OutboundOnly => NetworkClass::OutboundOnly, veilid_capnp::NetworkClass::WebApp => NetworkClass::WebApp, + veilid_capnp::NetworkClass::Invalid => NetworkClass::Invalid, } } diff --git a/veilid-core/src/rpc_processor/coders/node_info.rs b/veilid-core/src/rpc_processor/coders/node_info.rs index 72e7f31e..02f9bd78 100644 --- a/veilid-core/src/rpc_processor/coders/node_info.rs +++ b/veilid-core/src/rpc_processor/coders/node_info.rs @@ -31,18 +31,10 @@ pub fn encode_node_info( encode_dial_info_detail(&node_info.dial_info_detail_list[idx], &mut did_builder)?; } - if let Some(rpi) = &node_info.relay_peer_info { - let mut rpi_builder = builder.reborrow().init_relay_peer_info(); - encode_peer_info(rpi, &mut rpi_builder)?; - } - Ok(()) } -pub fn decode_node_info( - reader: &veilid_capnp::node_info::Reader, - allow_relay_peer_info: bool, -) -> Result { +pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result { let network_class = decode_network_class( reader .reborrow() @@ -81,22 +73,6 @@ pub fn decode_node_info( dial_info_detail_list.push(decode_dial_info_detail(&did)?) } - let relay_peer_info = if allow_relay_peer_info { - if reader.has_relay_peer_info() { - Some(Box::new(decode_peer_info( - &reader - .reborrow() - .get_relay_peer_info() - .map_err(RPCError::protocol)?, - false, - )?)) - } else { - None - } - } else { - None - }; - Ok(NodeInfo { network_class, outbound_protocols, @@ -104,6 +80,5 @@ pub fn decode_node_info( min_version, max_version, dial_info_detail_list, - relay_peer_info, }) } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation.rs b/veilid-core/src/rpc_processor/coders/operations/operation.rs index 9d36c192..a33ab29c 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation.rs @@ -120,7 +120,7 @@ impl RPCOperation { let sni_reader = operation_reader .get_sender_node_info() .map_err(RPCError::protocol)?; - let sni = decode_signed_node_info(&sni_reader, sender_node_id, true)?; + let sni = decode_signed_node_info(&sni_reader, sender_node_id)?; Some(sni) } else { None diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs b/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs index 6e561635..71fcba11 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_find_block.rs @@ -47,7 +47,7 @@ impl RPCOperationFindBlockA { .map_err(RPCError::map_internal("too many suppliers"))?, ); for s in suppliers_reader.iter() { - let peer_info = decode_peer_info(&s, true)?; + let peer_info = decode_peer_info(&s)?; suppliers.push(peer_info); } @@ -59,7 +59,7 @@ impl RPCOperationFindBlockA { .map_err(RPCError::map_internal("too many peers"))?, ); for p in peers_reader.iter() { - let peer_info = decode_peer_info(&p, true)?; + let peer_info = decode_peer_info(&p)?; peers.push(peer_info); } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs index 852700a0..bfd0ded9 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs @@ -41,7 +41,7 @@ impl RPCOperationFindNodeA { .map_err(RPCError::map_internal("too many peers"))?, ); for p in peers_reader.iter() { - let peer_info = decode_peer_info(&p, true)?; + let peer_info = decode_peer_info(&p)?; peers.push(peer_info); } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs index 9ac5f9f6..bb800511 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_get_value.rs @@ -48,7 +48,7 @@ impl RPCOperationGetValueA { .map_err(RPCError::map_internal("too many peers"))?, ); for p in peers_reader.iter() { - let peer_info = decode_peer_info(&p, true)?; + let peer_info = decode_peer_info(&p)?; peers.push(peer_info); } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs b/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs index a785e6b5..5f077816 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_node_info_update.rs @@ -18,7 +18,7 @@ impl RPCOperationNodeInfoUpdate { } let sender_node_id = opt_sender_node_id.unwrap(); let sni_reader = reader.get_signed_node_info().map_err(RPCError::protocol)?; - let signed_node_info = decode_signed_node_info(&sni_reader, sender_node_id, true)?; + let signed_node_info = decode_signed_node_info(&sni_reader, sender_node_id)?; Ok(RPCOperationNodeInfoUpdate { signed_node_info }) } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs index af5c5350..18c430d1 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs @@ -53,7 +53,7 @@ impl RPCOperationSetValueA { .map_err(RPCError::map_internal("too many peers"))?, ); for p in peers_reader.iter() { - let peer_info = decode_peer_info(&p, true)?; + let peer_info = decode_peer_info(&p)?; peers.push(peer_info); } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs b/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs index 0e496417..b094eaaf 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_supply_block.rs @@ -49,7 +49,7 @@ impl RPCOperationSupplyBlockA { .map_err(RPCError::map_internal("too many peers"))?, ); for p in peers_reader.iter() { - let peer_info = decode_peer_info(&p, true)?; + let peer_info = decode_peer_info(&p)?; peers.push(peer_info); } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs index 48d7d864..cbb08fcb 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs @@ -43,7 +43,7 @@ impl RPCOperationWatchValueA { .map_err(RPCError::map_internal("too many peers"))?, ); for p in peers_reader.iter() { - let peer_info = decode_peer_info(&p, true)?; + let peer_info = decode_peer_info(&p)?; peers.push(peer_info); } diff --git a/veilid-core/src/rpc_processor/coders/peer_info.rs b/veilid-core/src/rpc_processor/coders/peer_info.rs index 043fe761..428980f4 100644 --- a/veilid-core/src/rpc_processor/coders/peer_info.rs +++ b/veilid-core/src/rpc_processor/coders/peer_info.rs @@ -14,10 +14,7 @@ pub fn encode_peer_info( Ok(()) } -pub fn decode_peer_info( - reader: &veilid_capnp::peer_info::Reader, - allow_relay_peer_info: bool, -) -> Result { +pub fn decode_peer_info(reader: &veilid_capnp::peer_info::Reader) -> Result { let nid_reader = reader .reborrow() .get_node_id() @@ -27,8 +24,7 @@ pub fn decode_peer_info( .get_signed_node_info() .map_err(RPCError::protocol)?; let node_id = NodeId::new(decode_public_key(&nid_reader)); - let signed_node_info = - decode_signed_node_info(&sni_reader, &node_id.key, allow_relay_peer_info)?; + let signed_node_info = decode_signed_node_info(&sni_reader, &node_id.key)?; Ok(PeerInfo { node_id, diff --git a/veilid-core/src/rpc_processor/coders/private_safety_route.rs b/veilid-core/src/rpc_processor/coders/private_safety_route.rs index 4ac99409..31a8362b 100644 --- a/veilid-core/src/rpc_processor/coders/private_safety_route.rs +++ b/veilid-core/src/rpc_processor/coders/private_safety_route.rs @@ -77,7 +77,7 @@ pub fn decode_route_hop(reader: &veilid_capnp::route_hop::Reader) -> Result { let pi_reader = pi.map_err(RPCError::protocol)?; RouteNode::PeerInfo( - decode_peer_info(&pi_reader, true) + decode_peer_info(&pi_reader) .map_err(RPCError::map_protocol("invalid peer info in route hop"))?, ) } diff --git a/veilid-core/src/rpc_processor/coders/signal_info.rs b/veilid-core/src/rpc_processor/coders/signal_info.rs index d07ff7d4..7a272973 100644 --- a/veilid-core/src/rpc_processor/coders/signal_info.rs +++ b/veilid-core/src/rpc_processor/coders/signal_info.rs @@ -53,7 +53,7 @@ pub fn decode_signal_info( let pi_reader = r.get_peer_info().map_err(RPCError::map_protocol( "invalid peer info in hole punch signal info", ))?; - let peer_info = decode_peer_info(&pi_reader, true)?; + let peer_info = decode_peer_info(&pi_reader)?; SignalInfo::HolePunch { receipt, peer_info } } @@ -69,7 +69,7 @@ pub fn decode_signal_info( let pi_reader = r.get_peer_info().map_err(RPCError::map_protocol( "invalid peer info in reverse connect signal info", ))?; - let peer_info = decode_peer_info(&pi_reader, true)?; + let peer_info = decode_peer_info(&pi_reader)?; SignalInfo::ReverseConnect { receipt, peer_info } } diff --git a/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs new file mode 100644 index 00000000..1e9768f4 --- /dev/null +++ b/veilid-core/src/rpc_processor/coders/signed_direct_node_info.rs @@ -0,0 +1,43 @@ +use crate::*; +use rpc_processor::*; + +pub fn encode_signed_direct_node_info( + signed_direct_node_info: &SignedDirectNodeInfo, + builder: &mut veilid_capnp::signed_direct_node_info::Builder, +) -> Result<(), RPCError> { + // + let mut ni_builder = builder.reborrow().init_node_info(); + encode_node_info(&signed_direct_node_info.node_info, &mut ni_builder)?; + + builder + .reborrow() + .set_timestamp(signed_direct_node_info.timestamp); + + let mut sig_builder = builder.reborrow().init_signature(); + encode_signature(&signed_direct_node_info.signature, &mut sig_builder); + + Ok(()) +} + +pub fn decode_signed_direct_node_info( + reader: &veilid_capnp::signed_direct_node_info::Reader, + node_id: &DHTKey, +) -> Result { + let ni_reader = reader + .reborrow() + .get_node_info() + .map_err(RPCError::protocol)?; + let node_info = decode_node_info(&ni_reader)?; + + let sig_reader = reader + .reborrow() + .get_signature() + .map_err(RPCError::protocol)?; + + let timestamp = reader.reborrow().get_timestamp(); + + let signature = decode_signature(&sig_reader); + + SignedDirectNodeInfo::new(NodeId::new(*node_id), node_info, timestamp, signature) + .map_err(RPCError::protocol) +} diff --git a/veilid-core/src/rpc_processor/coders/signed_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_node_info.rs index 11c9d51c..64ae9c80 100644 --- a/veilid-core/src/rpc_processor/coders/signed_node_info.rs +++ b/veilid-core/src/rpc_processor/coders/signed_node_info.rs @@ -5,14 +5,16 @@ pub fn encode_signed_node_info( signed_node_info: &SignedNodeInfo, builder: &mut veilid_capnp::signed_node_info::Builder, ) -> Result<(), RPCError> { - // - let mut ni_builder = builder.reborrow().init_node_info(); - encode_node_info(&signed_node_info.node_info, &mut ni_builder)?; - - let mut sig_builder = builder.reborrow().init_signature(); - encode_signature(&signed_node_info.signature, &mut sig_builder); - - builder.reborrow().set_timestamp(signed_node_info.timestamp); + match signed_node_info { + SignedNodeInfo::Direct(d) => { + let mut d_builder = builder.reborrow().init_direct(); + encode_signed_direct_node_info(d, &mut d_builder)?; + } + SignedNodeInfo::Relayed(r) => { + let mut r_builder = builder.reborrow().init_relayed(); + encode_signed_relayed_node_info(r, &mut r_builder)?; + } + } Ok(()) } @@ -20,22 +22,20 @@ pub fn encode_signed_node_info( pub fn decode_signed_node_info( reader: &veilid_capnp::signed_node_info::Reader, node_id: &DHTKey, - allow_relay_peer_info: bool, ) -> Result { - let ni_reader = reader - .reborrow() - .get_node_info() - .map_err(RPCError::protocol)?; - let node_info = decode_node_info(&ni_reader, allow_relay_peer_info)?; - - let sig_reader = reader - .reborrow() - .get_signature() - .map_err(RPCError::protocol)?; - let signature = decode_signature(&sig_reader); - - let timestamp = reader.reborrow().get_timestamp(); - - SignedNodeInfo::new(node_info, NodeId::new(*node_id), signature, timestamp) - .map_err(RPCError::protocol) + match reader + .which() + .map_err(RPCError::map_internal("invalid signal operation"))? + { + veilid_capnp::signed_node_info::Direct(d) => { + let d_reader = d.map_err(RPCError::protocol)?; + let sdni = decode_signed_direct_node_info(&d_reader, node_id)?; + Ok(SignedNodeInfo::Direct(sdni)) + } + veilid_capnp::signed_node_info::Relayed(r) => { + let r_reader = r.map_err(RPCError::protocol)?; + let srni = decode_signed_relayed_node_info(&r_reader, node_id)?; + Ok(SignedNodeInfo::Relayed(srni)) + } + } } diff --git a/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs b/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs new file mode 100644 index 00000000..530f8254 --- /dev/null +++ b/veilid-core/src/rpc_processor/coders/signed_relayed_node_info.rs @@ -0,0 +1,67 @@ +use crate::*; +use rpc_processor::*; + +pub fn encode_signed_relayed_node_info( + signed_relayed_node_info: &SignedRelayedNodeInfo, + builder: &mut veilid_capnp::signed_relayed_node_info::Builder, +) -> Result<(), RPCError> { + // + let mut ni_builder = builder.reborrow().init_node_info(); + encode_node_info(&signed_relayed_node_info.node_info, &mut ni_builder)?; + + let mut rid_builder = builder.reborrow().init_relay_id(); + encode_public_key(&signed_relayed_node_info.relay_id.key, &mut rid_builder)?; + + let mut ri_builder = builder.reborrow().init_relay_info(); + encode_signed_direct_node_info(&signed_relayed_node_info.relay_info, &mut ri_builder)?; + + builder + .reborrow() + .set_timestamp(signed_relayed_node_info.timestamp); + + let mut sig_builder = builder.reborrow().init_signature(); + encode_signature(&signed_relayed_node_info.signature, &mut sig_builder); + + Ok(()) +} + +pub fn decode_signed_relayed_node_info( + reader: &veilid_capnp::signed_relayed_node_info::Reader, + node_id: &DHTKey, +) -> Result { + let ni_reader = reader + .reborrow() + .get_node_info() + .map_err(RPCError::protocol)?; + let node_info = decode_node_info(&ni_reader)?; + + let rid_reader = reader + .reborrow() + .get_relay_id() + .map_err(RPCError::protocol)?; + let relay_id = decode_public_key(&rid_reader); + + let ri_reader = reader + .reborrow() + .get_relay_info() + .map_err(RPCError::protocol)?; + let relay_info = decode_signed_direct_node_info(&ri_reader, &relay_id)?; + + let sig_reader = reader + .reborrow() + .get_signature() + .map_err(RPCError::protocol)?; + let timestamp = reader.reborrow().get_timestamp(); + + let signature = decode_signature(&sig_reader); + + SignedRelayedNodeInfo::new( + NodeId::new(*node_id), + node_info, + NodeId::new(relay_id), + relay_info, + timestamp, + signature, + ) + .map_err(RPCError::protocol) +} diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index f2186b05..4d0a6f85 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -619,7 +619,7 @@ impl RPCProcessor { // routing table caching when it is okay to do so // This is only done in the PublicInternet routing domain because // as far as we can tell this is the only domain that will really benefit - fn get_sender_signed_node_info(&self, dest: &Destination) -> Option { + fn get_sender_signed_node_info(&self, dest: &Destination) -> Option { // Don't do this if the sender is to remain private // Otherwise we would be attaching the original sender's identity to the final destination, // thus defeating the purpose of the safety route entirely :P @@ -682,7 +682,7 @@ impl RPCProcessor { let op_id = operation.op_id(); // Log rpc send - debug!(target: "rpc_message", dir = "send", kind = "question", op_id, desc = operation.kind().desc(), ?dest); + trace!(target: "rpc_message", dir = "send", kind = "question", op_id, desc = operation.kind().desc(), ?dest); // Produce rendered operation let RenderedOperation { @@ -745,7 +745,7 @@ impl RPCProcessor { let operation = RPCOperation::new_statement(statement, opt_sender_info); // Log rpc send - debug!(target: "rpc_message", dir = "send", kind = "statement", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest); + trace!(target: "rpc_message", dir = "send", kind = "statement", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest); // Produce rendered operation let RenderedOperation { @@ -865,7 +865,7 @@ impl RPCProcessor { let operation = RPCOperation::new_answer(&request.operation, answer, opt_sender_info); // Log rpc send - debug!(target: "rpc_message", dir = "send", kind = "answer", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest); + trace!(target: "rpc_message", dir = "send", kind = "answer", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest); // Produce rendered operation let RenderedOperation { @@ -997,7 +997,7 @@ impl RPCProcessor { }; // Log rpc receive - debug!(target: "rpc_message", dir = "recv", kind, op_id = msg.operation.op_id(), desc = msg.operation.kind().desc(), header = ?msg.header); + trace!(target: "rpc_message", dir = "recv", kind, op_id = msg.operation.op_id(), desc = msg.operation.kind().desc(), header = ?msg.header); // Process specific message kind match msg.operation.kind() { diff --git a/veilid-core/src/tests/common/test_table_store.rs b/veilid-core/src/tests/common/test_table_store.rs index f10f322e..5e3478a3 100644 --- a/veilid-core/src/tests/common/test_table_store.rs +++ b/veilid-core/src/tests/common/test_table_store.rs @@ -122,21 +122,21 @@ pub async fn test_store_delete_load(ts: TableStore) { assert_eq!(db.load(2, b"baz").unwrap(), Some(b"QWERTY".to_vec())); } -pub async fn test_cbor(ts: TableStore) { - trace!("test_cbor"); +pub async fn test_frozen(ts: TableStore) { + trace!("test_frozen"); let _ = ts.delete("test"); let db = ts.open("test", 3).await.expect("should have opened"); let (dht_key, _) = generate_secret(); - assert!(db.store_cbor(0, b"asdf", &dht_key).is_ok()); + assert!(db.store_rkyv(0, b"asdf", &dht_key).is_ok()); - assert_eq!(db.load_cbor::(0, b"qwer").unwrap(), None); + assert_eq!(db.load_rkyv::(0, b"qwer").unwrap(), None); - let d = match db.load_cbor::(0, b"asdf") { + let d = match db.load_rkyv::(0, b"asdf") { Ok(x) => x, Err(e) => { - panic!("couldn't decode cbor: {}", e); + panic!("couldn't decode: {}", e); } }; assert_eq!(d, Some(dht_key), "keys should be equal"); @@ -147,8 +147,8 @@ pub async fn test_cbor(ts: TableStore) { ); assert!( - db.load_cbor::(1, b"foo").is_err(), - "should fail to load cbor" + db.load_rkyv::(1, b"foo").is_err(), + "should fail to unfreeze" ); } @@ -157,7 +157,7 @@ pub async fn test_all() { let ts = api.table_store().unwrap(); test_delete_open_delete(ts.clone()).await; test_store_delete_load(ts.clone()).await; - test_cbor(ts.clone()).await; + test_frozen(ts.clone()).await; let _ = ts.delete("test").await; diff --git a/veilid-core/src/tests/common/test_veilid_core.rs b/veilid-core/src/tests/common/test_veilid_core.rs index e84828a0..bf8d6915 100644 --- a/veilid-core/src/tests/common/test_veilid_core.rs +++ b/veilid-core/src/tests/common/test_veilid_core.rs @@ -43,7 +43,78 @@ pub async fn test_attach_detach() { 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"); + + // Test direct + let node_info = NodeInfo { + network_class: NetworkClass::InboundCapable, + outbound_protocols: ProtocolTypeSet::all(), + address_types: AddressTypeSet::all(), + min_version: 0, + max_version: 0, + dial_info_detail_list: vec![DialInfoDetail { + class: DialInfoClass::Mapped, + dial_info: DialInfo::udp(SocketAddress::default()), + }], + }; + + let (pkey, skey) = generate_secret(); + + let sni = + SignedDirectNodeInfo::with_secret(NodeId::new(pkey.clone()), node_info.clone(), &skey) + .unwrap(); + let _ = SignedDirectNodeInfo::new( + NodeId::new(pkey), + node_info.clone(), + sni.timestamp, + sni.signature, + ) + .unwrap(); + + // Test relayed + let node_info2 = NodeInfo { + network_class: NetworkClass::OutboundOnly, + outbound_protocols: ProtocolTypeSet::all(), + address_types: AddressTypeSet::all(), + min_version: 0, + max_version: 0, + dial_info_detail_list: vec![DialInfoDetail { + class: DialInfoClass::Blocked, + dial_info: DialInfo::udp(SocketAddress::default()), + }], + }; + + let (pkey2, skey2) = generate_secret(); + + let sni2 = SignedRelayedNodeInfo::with_secret( + NodeId::new(pkey2.clone()), + node_info2.clone(), + NodeId::new(pkey.clone()), + sni.clone(), + &skey2, + ) + .unwrap(); + let _ = SignedRelayedNodeInfo::new( + NodeId::new(pkey2), + node_info2, + NodeId::new(pkey), + sni, + sni2.timestamp, + sni2.signature, + ) + .unwrap(); + + api.shutdown().await; +} + pub async fn test_all() { test_startup_shutdown().await; test_attach_detach().await; + test_signed_node_info().await; } diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 9bc78d7d..9405db24 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -4,7 +4,6 @@ use super::*; use data_encoding::BASE64URL_NOPAD; use routing_table::*; -use rpc_processor::*; #[derive(Default, Debug)] struct DebugCache { diff --git a/veilid-core/src/veilid_api/mod.rs b/veilid-core/src/veilid_api/mod.rs index b28bc49b..9f522aba 100644 --- a/veilid-core/src/veilid_api/mod.rs +++ b/veilid-core/src/veilid_api/mod.rs @@ -30,7 +30,7 @@ pub use routing_table::{NodeRef, NodeRefBase, RoutingTable}; use core::fmt; use core_context::{api_shutdown, VeilidCoreContext}; use enumset::*; -use rpc_processor::RPCProcessor; +use rpc_processor::*; use serde::*; use xx::*; @@ -60,7 +60,21 @@ macro_rules! apibail_parse { }; } -#[derive(ThisError, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[derive( + ThisError, + Clone, + Debug, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(u8), derive(CheckBytes))] #[serde(tag = "kind")] pub enum VeilidAPIError { #[error("Not initialized")] @@ -159,7 +173,21 @@ impl VeilidAPIError { ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Serialize, Deserialize)] +#[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, @@ -220,14 +248,20 @@ impl fmt::Display for VeilidLogLevel { } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[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, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[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")] @@ -237,7 +271,10 @@ pub struct VeilidAppMessage { pub message: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[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")] @@ -250,19 +287,28 @@ pub struct VeilidAppCall { pub id: u64, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct VeilidStateAttachment { pub state: AttachmentState, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct PeerTableData { pub node_id: DHTKey, pub peer_address: PeerAddress, pub peer_stats: PeerStats, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[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")] @@ -272,7 +318,8 @@ pub struct VeilidStateNetwork { pub peers: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(u8), derive(CheckBytes))] #[serde(tag = "kind")] pub enum VeilidUpdate { Log(VeilidLog), @@ -283,7 +330,8 @@ pub enum VeilidUpdate { Shutdown, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct VeilidState { pub attachment: VeilidStateAttachment, pub network: VeilidStateNetwork, @@ -291,7 +339,21 @@ pub struct VeilidState { ///////////////////////////////////////////////////////////////////////////////////////////////////// /// -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct NodeId { pub key: DHTKey, } @@ -315,7 +377,21 @@ impl FromStr for NodeId { } } -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct ValueKey { pub key: DHTKey, pub subkey: Option, @@ -336,7 +412,21 @@ impl ValueKey { } } -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct ValueData { pub data: Vec, pub seq: u32, @@ -354,7 +444,21 @@ impl ValueData { } } -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[derive( + Clone, + Debug, + Default, + PartialOrd, + PartialEq, + Eq, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct BlockId { pub key: DHTKey, } @@ -367,7 +471,22 @@ impl BlockId { ///////////////////////////////////////////////////////////////////////////////////////////////////// // Keep member order appropriate for sorting < preference -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +#[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 @@ -401,7 +520,22 @@ impl DialInfoClass { } // Ordering here matters, >= is used to check strength of sequencing requirement -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[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, @@ -409,14 +543,44 @@ pub enum Sequencing { } // Ordering here matters, >= is used to check strength of stability requirement -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[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, } /// The choice of safety route to include in compiled routes -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[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), @@ -425,7 +589,22 @@ pub enum SafetySelection { } /// Options for safety routes (sender privacy) -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[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 if it still exists pub preferred_route: Option, @@ -438,7 +617,21 @@ pub struct SafetySpec { } // Keep member order appropriate for sorting < preference -#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize, Hash)] +#[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, @@ -468,7 +661,22 @@ impl DialInfoDetail { > = None:: core::cmp::Ordering>; } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +#[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 @@ -493,7 +701,10 @@ impl NetworkClass { /// is returned by the StatusA call /// PublicInternet RoutingDomain Status -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[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, @@ -502,13 +713,17 @@ pub struct PublicInternetNodeStatus { pub will_validate_dial_info: bool, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[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)] +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(u8), derive(CheckBytes))] pub enum NodeStatus { PublicInternet(PublicInternetNodeStatus), LocalNetwork(LocalNetworkNodeStatus), @@ -547,181 +762,57 @@ impl NodeStatus { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[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 min_version: u8, pub max_version: u8, pub dial_info_detail_list: Vec, - pub relay_peer_info: Option>, -} - -impl NodeInfo { - pub fn first_filtered_dial_info_detail( - &self, - sort: Option, - filter: F, - ) -> Option - 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( - &self, - sort: Option, - filter: F, - ) -> Vec - 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 - } - - pub fn has_any_dial_info(&self) -> bool { - !self.dial_info_detail_list.is_empty() - || !self - .relay_peer_info - .as_ref() - .map(|rpi| rpi.signed_node_info.node_info.has_direct_dial_info()) - .unwrap_or_default() - } - - pub fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool { - // Check our dial info - for did in &self.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_peer_info - .as_ref() - .map(|rpi| { - let relay_ni = &rpi.signed_node_info.node_info; - 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(); - } - - pub fn has_direct_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() - } } #[allow(clippy::derive_hash_xor_eq)] -#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)] +#[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; -#[allow(clippy::derive_hash_xor_eq)] -#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)] // 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, @@ -734,10 +825,23 @@ impl LowLevelProtocolType { } pub type LowLevelProtocolTypeSet = EnumSet; -#[allow(clippy::derive_hash_xor_eq)] -#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)] // 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, @@ -794,10 +898,24 @@ impl ProtocolType { ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS } } + pub type ProtocolTypeSet = EnumSet; #[allow(clippy::derive_hash_xor_eq)] -#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)] +#[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, @@ -806,7 +924,20 @@ pub type AddressTypeSet = EnumSet; // Routing domain here is listed in order of preference, keep in order #[allow(clippy::derive_hash_xor_eq)] -#[derive(Debug, Ord, PartialOrd, Hash, Serialize, Deserialize, EnumSetType)] +#[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, @@ -822,7 +953,22 @@ impl RoutingDomain { } pub type RoutingDomainSet = EnumSet; -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)] +#[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), @@ -937,8 +1083,22 @@ impl FromStr for Address { } #[derive( - Copy, Default, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize, + 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, @@ -994,9 +1154,24 @@ impl FromStr for SocketAddress { ////////////////////////////////////////////////////////////////// -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[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, } @@ -1063,32 +1238,106 @@ pub trait MatchesDialInfoFilter { fn matches_filter(&self, filter: &DialInfoFilter) -> bool; } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize, Hash)] +#[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, } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize, Hash)] +#[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, } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize, Hash)] +#[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, } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize, Hash)] +#[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, } -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize, Hash)] -#[serde(tag = "kind")] // 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), @@ -1601,57 +1850,63 @@ impl MatchesDialInfoFilter for DialInfo { ////////////////////////////////////////////////////////////////////////// // Signed NodeInfo that can be passed around amongst peers and verifiable -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SignedNodeInfo { +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] +pub struct SignedDirectNodeInfo { pub node_info: NodeInfo, - pub signature: DHTSignature, pub timestamp: u64, + pub signature: DHTSignature, } -impl SignedNodeInfo { +impl SignedDirectNodeInfo { pub fn new( - node_info: NodeInfo, node_id: NodeId, - signature: DHTSignature, + node_info: NodeInfo, timestamp: u64, + signature: DHTSignature, ) -> Result { - let mut node_info_bytes = serde_cbor::to_vec(&node_info) - .map_err(|e| VeilidAPIError::parse_error("failed to encode node info as cbor", e))?; - let mut timestamp_bytes = serde_cbor::to_vec(×tamp) - .map_err(|e| VeilidAPIError::parse_error("failed to encode timestamp as cbor", e))?; - - node_info_bytes.append(&mut timestamp_bytes); - + let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; verify(&node_id.key, &node_info_bytes, &signature)?; Ok(Self { node_info, - signature, timestamp, + signature, }) } pub fn with_secret( - node_info: NodeInfo, node_id: NodeId, + node_info: NodeInfo, secret: &DHTKeySecret, ) -> Result { let timestamp = intf::get_timestamp(); - - let mut node_info_bytes = serde_cbor::to_vec(&node_info) - .map_err(|e| VeilidAPIError::parse_error("failed to encode node info as cbor", e))?; - let mut timestamp_bytes = serde_cbor::to_vec(×tamp) - .map_err(|e| VeilidAPIError::parse_error("failed to encode timestamp as cbor", e))?; - - node_info_bytes.append(&mut timestamp_bytes); - + let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; let signature = sign(&node_id.key, secret, &node_info_bytes)?; Ok(Self { node_info, - signature, timestamp, + signature, }) } + fn make_signature_bytes( + node_info: &NodeInfo, + timestamp: u64, + ) -> Result, 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::(); + 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.to_le_bytes().to_vec()); + + Ok(node_info_bytes) + } + pub fn with_no_signature(node_info: NodeInfo) -> Self { Self { node_info, @@ -1665,7 +1920,136 @@ impl SignedNodeInfo { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +/// 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_id: NodeId, + pub relay_info: SignedDirectNodeInfo, + pub timestamp: u64, + pub signature: DHTSignature, +} + +impl SignedRelayedNodeInfo { + pub fn new( + node_id: NodeId, + node_info: NodeInfo, + relay_id: NodeId, + relay_info: SignedDirectNodeInfo, + timestamp: u64, + signature: DHTSignature, + ) -> Result { + let node_info_bytes = + Self::make_signature_bytes(&node_info, &relay_id, &relay_info, timestamp)?; + verify(&node_id.key, &node_info_bytes, &signature)?; + Ok(Self { + node_info, + relay_id, + relay_info, + signature, + timestamp, + }) + } + + pub fn with_secret( + node_id: NodeId, + node_info: NodeInfo, + relay_id: NodeId, + relay_info: SignedDirectNodeInfo, + secret: &DHTKeySecret, + ) -> Result { + let timestamp = intf::get_timestamp(); + let node_info_bytes = + Self::make_signature_bytes(&node_info, &relay_id, &relay_info, timestamp)?; + let signature = sign(&node_id.key, secret, &node_info_bytes)?; + Ok(Self { + node_info, + relay_id, + relay_info, + signature, + timestamp, + }) + } + + fn make_signature_bytes( + node_info: &NodeInfo, + relay_id: &NodeId, + relay_info: &SignedDirectNodeInfo, + timestamp: u64, + ) -> Result, 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::(); + 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 id to signature + let mut rid_msg = ::capnp::message::Builder::new_default(); + let mut rid_builder = rid_msg.init_root::(); + encode_public_key(&relay_id.key, &mut rid_builder).map_err(VeilidAPIError::internal)?; + 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::(); + 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.to_le_bytes().to_vec()); + + Ok(sig_bytes) + } +} + +#[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_valid_signature(&self) -> bool { + match self { + SignedNodeInfo::Direct(d) => d.has_valid_signature(), + SignedNodeInfo::Relayed(r) => true, + } + } + + pub fn timestamp(&self) -> u64 { + 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_id(&self) -> Option { + match self { + SignedNodeInfo::Direct(d) => None, + SignedNodeInfo::Relayed(r) => Some(r.relay_id.clone()), + } + } + pub fn relay_info(&self) -> Option<&NodeInfo> { + match self { + SignedNodeInfo::Direct(d) => None, + SignedNodeInfo::Relayed(r) => Some(&r.relay_info.node_info), + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct PeerInfo { pub node_id: NodeId, pub signed_node_info: SignedNodeInfo, @@ -1680,7 +2064,177 @@ impl PeerInfo { } } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +impl PeerInfo { + /* + pub fn first_filtered_dial_info_detail( + &self, + sort: Option, + filter: F, + ) -> Option + 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( + &self, + sort: Option, + filter: F, + ) -> Vec + 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 + } + + pub fn has_any_dial_info(&self) -> bool { + !self.dial_info_detail_list.is_empty() + || !self + .relay_peer_info + .as_ref() + .map(|rpi| rpi.signed_node_info.node_info.has_direct_dial_info()) + .unwrap_or_default() + } + + pub fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool { + // Check our dial info + for did in &self.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_peer_info + .as_ref() + .map(|rpi| { + let relay_ni = &rpi.signed_node_info.node_info; + 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(); + } + + pub fn has_direct_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() + } + + */ +} + +#[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")] @@ -1719,7 +2273,22 @@ impl PeerAddress { /// 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)] +#[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, @@ -1778,7 +2347,21 @@ impl MatchesDialInfoFilter for ConnectionDescriptor { ////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive( + Clone, + Debug, + Default, + Eq, + PartialEq, + PartialOrd, + Ord, + Serialize, + Deserialize, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct NodeDialInfo { pub node_id: NodeId, pub dial_info: DialInfo, @@ -1813,7 +2396,19 @@ impl FromStr for NodeDialInfo { } } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] +#[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: u64, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies @@ -1823,7 +2418,19 @@ pub struct LatencyStats { pub slowest: u64, // slowest latency in the ROLLING_LATENCIES_SIZE last latencies } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] +#[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: u64, // total amount transferred ever @@ -1835,13 +2442,37 @@ pub struct TransferStats { pub minimum: u64, // minimum rate over the ROLLING_TRANSFERS_SIZE last amounts } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] +#[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, Serialize, Deserialize, PartialEq, Eq)] +#[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 @@ -1856,7 +2487,19 @@ pub struct RPCStats { pub failed_to_send: u32, // number of messages that have failed to send since we last successfully sent one } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] +#[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: u64, // when the peer was added to the routing table @@ -1870,7 +2513,8 @@ pub type ValueChangeCallback = ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(u8), derive(CheckBytes))] pub enum SignalInfo { HolePunch { // UDP Hole Punch Request @@ -1887,13 +2531,41 @@ pub enum SignalInfo { } ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[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)] +#[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 @@ -1903,7 +2575,8 @@ pub enum TunnelError { pub type TunnelId = u64; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[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 @@ -1918,7 +2591,10 @@ impl Default for TunnelEndpoint { } } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct FullTunnel { pub id: TunnelId, pub timeout: u64, @@ -1926,7 +2602,10 @@ pub struct FullTunnel { pub remote: TunnelEndpoint, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes))] pub struct PartialTunnel { pub id: TunnelId, pub timeout: u64, diff --git a/veilid-core/src/veilid_api/serialize_helpers.rs b/veilid-core/src/veilid_api/serialize_helpers.rs index 3f843e4b..bb8fb045 100644 --- a/veilid-core/src/veilid_api/serialize_helpers.rs +++ b/veilid-core/src/veilid_api/serialize_helpers.rs @@ -1,5 +1,9 @@ use super::*; +pub use bytecheck::CheckBytes; use core::fmt::Debug; +pub use rkyv::Archive as RkyvArchive; +pub use rkyv::Deserialize as RkyvDeserialize; +pub use rkyv::Serialize as RkyvSerialize; // XXX: Don't trace these functions as they are used in the transfer of API logs, which will recurse! @@ -128,3 +132,79 @@ pub mod arc_serialize { Ok(Arc::new(T::deserialize(d)?)) } } + +pub fn to_rkyv(v: &T) -> EyreResult> +where + T: RkyvSerialize>, +{ + Ok(rkyv::to_bytes::(v) + .wrap_err("failed to freeze object")? + .to_vec()) +} + +pub fn from_rkyv(v: Vec) -> EyreResult +where + T: RkyvArchive, + ::Archived: + for<'t> bytecheck::CheckBytes>, + ::Archived: + rkyv::Deserialize, +{ + match rkyv::from_bytes::(&v) { + Ok(v) => Ok(v), + Err(e) => { + bail!("failed to deserialize frozen object: {}", e); + } + } +} + +pub struct RkyvEnumSet; + +impl rkyv::with::ArchiveWith> for RkyvEnumSet +where + T: EnumSetType + EnumSetTypeWithRepr, + ::Repr: rkyv::Archive, +{ + type Archived = rkyv::Archived<::Repr>; + type Resolver = rkyv::Resolver<::Repr>; + + #[inline] + unsafe fn resolve_with( + field: &EnumSet, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + let r = field.as_repr(); + r.resolve(pos, resolver, out); + } +} + +impl rkyv::with::SerializeWith, S> for RkyvEnumSet +where + S: rkyv::Fallible + ?Sized, + T: EnumSetType + EnumSetTypeWithRepr, + ::Repr: rkyv::Serialize, +{ + fn serialize_with(field: &EnumSet, serializer: &mut S) -> Result { + let r = field.as_repr(); + r.serialize(serializer) + } +} + +impl + rkyv::with::DeserializeWith::Repr>, EnumSet, D> + for RkyvEnumSet +where + D: rkyv::Fallible + ?Sized, + T: EnumSetType + EnumSetTypeWithRepr, + ::Repr: rkyv::Archive, + rkyv::Archived<::Repr>: rkyv::Deserialize, D>, +{ + fn deserialize_with( + field: &rkyv::Archived<::Repr>, + deserializer: &mut D, + ) -> Result, D::Error> { + Ok(field.deserialize(deserializer)?.into()) + } +}