From 25ace50d459130a7e50c6b3d991ede17969614a6 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sat, 26 Nov 2022 14:16:02 -0500 Subject: [PATCH] break everything --- Cargo.lock | 32 +-- veilid-core/Cargo.toml | 13 +- veilid-core/src/crypto/envelope.rs | 34 ++- veilid-core/src/crypto/receipt.rs | 31 +-- veilid-core/src/intf/wasm/protected_store.rs | 50 ++-- .../src/routing_table/route_spec_store.rs | 4 +- veilid-core/src/veilid_api/debug.rs | 12 +- veilid-core/src/veilid_api/mod.rs | 122 +++++----- veilid-core/src/veilid_api/routing_context.rs | 33 +-- veilid-core/src/veilid_config.rs | 6 +- veilid-core/src/xx/ip_extra.rs | 1 - veilid-core/src/xx/mod.rs | 7 +- veilid-flutter/lib/veilid.dart | 80 ++++++- veilid-flutter/lib/veilid_ffi.dart | 207 +++++++++++++++- veilid-flutter/lib/veilid_js.dart | 98 +++++++- veilid-flutter/rust/src/dart_ffi.rs | 224 +++++++++++++++++- veilid-wasm/src/lib.rs | 26 +- 17 files changed, 760 insertions(+), 220 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df0254c1..a9eb7648 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3145,14 +3145,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "no-std-net" -version = "0.6.0" -dependencies = [ - "serde", - "serde_test", -] - [[package]] name = "nom" version = "5.1.2" @@ -4140,7 +4132,7 @@ dependencies = [ [[package]] name = "rkyv" version = "0.7.39" -source = "git+https://github.com/crioux/rkyv.git?branch=issue_326#2f19cfac9f31a15e2fe74ad362eec7b011dc35b9" +source = "git+https://github.com/rkyv/rkyv.git?rev=57e2a8d#57e2a8daff3e6381e170e723ed1beea5c113b232" dependencies = [ "bytecheck", "hashbrown", @@ -4153,7 +4145,7 @@ dependencies = [ [[package]] name = "rkyv_derive" version = "0.7.39" -source = "git+https://github.com/crioux/rkyv.git?branch=issue_326#2f19cfac9f31a15e2fe74ad362eec7b011dc35b9" +source = "git+https://github.com/rkyv/rkyv.git?rev=57e2a8d#57e2a8daff3e6381e170e723ed1beea5c113b232" dependencies = [ "proc-macro2", "quote", @@ -4461,15 +4453,6 @@ 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" @@ -4513,15 +4496,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_test" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641666500e4e6fba7b91b73651a375cb53579468ab3c38389289b802797cad94" -dependencies = [ - "serde", -] - [[package]] name = "serde_yaml" version = "0.9.14" @@ -5615,7 +5589,6 @@ dependencies = [ "ndk 0.6.0", "ndk-glue", "nix 0.25.0", - "no-std-net", "once_cell", "owning_ref", "owo-colors", @@ -5631,7 +5604,6 @@ dependencies = [ "send_wrapper 0.6.0", "serde", "serde-big-array", - "serde_bytes", "serde_json", "serial_test", "simplelog 0.12.0", diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index e0ec79c8..3e0ebf31 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -34,6 +34,8 @@ secrecy = "^0" chacha20poly1305 = "^0" chacha20 = "^0" hashlink = { path = "../external/hashlink", features = ["serde_impl"] } +serde = { version = "^1", features = ["derive" ] } +serde_json = { version = "^1" } serde-big-array = "^0" futures-util = { version = "^0", default_features = false, features = ["alloc"] } parking_lot = "^0" @@ -59,9 +61,8 @@ 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", "validation"] } -rkyv = { git = "https://github.com/crioux/rkyv.git", branch = "issue_326", default_features = false, features = ["std", "alloc", "strict", "size_64", "validation"] } +#rkyv = { version = "^0", default_features = false, features = ["std", "alloc", "strict", "size_32", "validation"] } +rkyv = { git = "https://github.com/rkyv/rkyv.git", rev = "57e2a8d", default_features = false, features = ["std", "alloc", "strict", "size_32", "validation"] } bytecheck = "^0" # Dependencies for native builds only @@ -85,8 +86,7 @@ rustls-pemfile = "^0.2" futures-util = { version = "^0", default-features = false, features = ["async-await", "sink", "std", "io"] } keyvaluedb-sqlite = { path = "../external/keyvaluedb/keyvaluedb-sqlite" } data-encoding = { version = "^2" } -serde = { version = "^1", features = ["derive" ] } -serde_json = { version = "^1" } + socket2 = "^0" bugsalot = "^0" chrono = "^0" @@ -98,11 +98,8 @@ nix = "^0" wasm-bindgen = "^0" js-sys = "^0" wasm-bindgen-futures = "^0" -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_json = { version = "^1", default-features = false, features = ["alloc"] } getrandom = { version = "^0", features = ["js"] } ws_stream_wasm = "^0" async_executors = { version = "^0", default-features = false, features = [ "bindgen", "timer" ]} diff --git a/veilid-core/src/crypto/envelope.rs b/veilid-core/src/crypto/envelope.rs index 32a02447..ea84e5c6 100644 --- a/veilid-core/src/crypto/envelope.rs +++ b/veilid-core/src/crypto/envelope.rs @@ -76,7 +76,7 @@ impl Envelope { // Ensure we are at least the length of the envelope // Silent drop here, as we use zero length packets as part of the protocol for hole punching if data.len() < MIN_ENVELOPE_SIZE { - return Err(VeilidAPIError::generic("envelope data too small")); + apibail_generic!("envelope data too small"); } // Verify magic number @@ -84,31 +84,28 @@ impl Envelope { .try_into() .map_err(VeilidAPIError::internal)?; if magic != *ENVELOPE_MAGIC { - return Err(VeilidAPIError::generic("bad magic number")); + apibail_generic!("bad magic number"); } // Check version let version = data[0x04]; if version > MAX_CRYPTO_VERSION || version < MIN_CRYPTO_VERSION { - return Err(VeilidAPIError::parse_error( - "unsupported cryptography version", - version, - )); + apibail_parse_error!("unsupported cryptography version", version); } // Get min version let min_version = data[0x05]; if min_version > version { - return Err(VeilidAPIError::parse_error("version too low", version)); + apibail_parse_error!("version too low", version); } // Get max version let max_version = data[0x06]; if version > max_version { - return Err(VeilidAPIError::parse_error("version too high", version)); + apibail_parse_error!("version too high", version); } if min_version > max_version { - return Err(VeilidAPIError::generic("version information invalid")); + apibail_generic!("version information invalid"); } // Get size and ensure it matches the size of the envelope and is less than the maximum message size @@ -118,17 +115,17 @@ impl Envelope { .map_err(VeilidAPIError::internal)?, ); if (size as usize) > MAX_ENVELOPE_SIZE { - return Err(VeilidAPIError::parse_error("envelope too large", size)); + apibail_parse_error!("envelope too large", size); } if (size as usize) != data.len() { - return Err(VeilidAPIError::parse_error( + apibail_parse_error!( "size doesn't match envelope size", format!( "size doesn't match envelope size: size={} data.len()={}", size, data.len() - ), - )); + ) + ); } // Get the timestamp @@ -153,10 +150,10 @@ impl Envelope { // Ensure sender_id and recipient_id are not the same if sender_id == recipient_id { - return Err(VeilidAPIError::parse_error( + apibail_parse_error!( "sender_id should not be same as recipient_id", - recipient_id.encode(), - )); + recipient_id.encode() + ); } // Get signature @@ -206,10 +203,7 @@ impl Envelope { // Ensure body isn't too long let envelope_size: usize = body.len() + MIN_ENVELOPE_SIZE; if envelope_size > MAX_ENVELOPE_SIZE { - return Err(VeilidAPIError::parse_error( - "envelope size is too large", - envelope_size, - )); + apibail_parse_error!("envelope size is too large", envelope_size); } let mut data = vec![0u8; envelope_size]; diff --git a/veilid-core/src/crypto/receipt.rs b/veilid-core/src/crypto/receipt.rs index d67fea51..b622d2f9 100644 --- a/veilid-core/src/crypto/receipt.rs +++ b/veilid-core/src/crypto/receipt.rs @@ -59,10 +59,10 @@ impl Receipt { extra_data: D, ) -> Result { if extra_data.as_ref().len() > MAX_EXTRA_DATA_SIZE { - return Err(VeilidAPIError::parse_error( + apibail_parse_error!( "extra data too large for receipt", - extra_data.as_ref().len(), - )); + extra_data.as_ref().len() + ); } Ok(Self { version, @@ -75,7 +75,7 @@ impl Receipt { pub fn from_signed_data(data: &[u8]) -> Result { // Ensure we are at least the length of the envelope if data.len() < MIN_RECEIPT_SIZE { - return Err(VeilidAPIError::parse_error("receipt too small", data.len())); + apibail_parse_error!("receipt too small", data.len()); } // Verify magic number @@ -83,16 +83,13 @@ impl Receipt { .try_into() .map_err(VeilidAPIError::internal)?; if magic != *RECEIPT_MAGIC { - return Err(VeilidAPIError::generic("bad magic number")); + apibail_generic!("bad magic number"); } // Check version let version = data[0x04]; if version > MAX_CRYPTO_VERSION || version < MIN_CRYPTO_VERSION { - return Err(VeilidAPIError::parse_error( - "unsupported cryptography version", - version, - )); + apibail_parse_error!("unsupported cryptography version", version); } // Get size and ensure it matches the size of the envelope and is less than the maximum message size @@ -102,16 +99,13 @@ impl Receipt { .map_err(VeilidAPIError::internal)?, ); if (size as usize) > MAX_RECEIPT_SIZE { - return Err(VeilidAPIError::parse_error( - "receipt size is too large", - size, - )); + apibail_parse_error!("receipt size is too large", size); } if (size as usize) != data.len() { - return Err(VeilidAPIError::parse_error( + apibail_parse_error!( "size doesn't match receipt size", - format!("size={} data.len()={}", size, data.len()), - )); + format!("size={} data.len()={}", size, data.len()) + ); } // Get sender id @@ -153,10 +147,7 @@ impl Receipt { // Ensure extra data isn't too long let receipt_size: usize = self.extra_data.len() + MIN_RECEIPT_SIZE; if receipt_size > MAX_RECEIPT_SIZE { - return Err(VeilidAPIError::parse_error( - "receipt too large", - receipt_size, - )); + apibail_parse_error!("receipt too large", receipt_size); } let mut data: Vec = vec![0u8; receipt_size]; diff --git a/veilid-core/src/intf/wasm/protected_store.rs b/veilid-core/src/intf/wasm/protected_store.rs index 67a4d1da..e736739d 100644 --- a/veilid-core/src/intf/wasm/protected_store.rs +++ b/veilid-core/src/intf/wasm/protected_store.rs @@ -2,10 +2,7 @@ use super::*; use crate::xx::*; use crate::*; use data_encoding::BASE64URL_NOPAD; -use js_sys::*; -use send_wrapper::*; -use serde::{Deserialize, Serialize}; -use wasm_bindgen_futures::*; +use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use web_sys::*; #[derive(Clone)] @@ -44,15 +41,6 @@ impl ProtectedStore { #[instrument(level = "debug", skip(self))] pub async fn terminate(&self) {} - fn keyring_name(&self) -> String { - let c = self.config.get(); - if c.namespace.is_empty() { - "veilid_protected_store".to_owned() - } else { - format!("veilid_protected_store_{}", c.namespace) - } - } - fn browser_key_name(&self, key: &str) -> String { let c = self.config.get(); if c.namespace.is_empty() { @@ -136,22 +124,31 @@ impl ProtectedStore { } #[instrument(level = "trace", skip(self, value))] - pub async fn save_user_secret_frozen(&self, key: &str, value: &T) -> EyreResult + pub async fn save_user_secret_rkyv(&self, key: &str, value: &T) -> EyreResult where T: RkyvSerialize>, { - let v = to_frozen(value)?; + 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_frozen(&self, key: &str) -> EyreResult> + pub async fn load_user_secret_rkyv(&self, key: &str) -> EyreResult> where T: RkyvArchive, ::Archived: for<'t> bytecheck::CheckBytes>, ::Archived: - rkyv::Deserialize, + RkyvDeserialize, { let out = self.load_user_secret(key).await?; let b = match out { @@ -161,7 +158,24 @@ impl ProtectedStore { } }; - let obj = from_frozen(&b)?; + 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/routing_table/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store.rs index e0167291..e9c0681f 100644 --- a/veilid-core/src/routing_table/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store.rs @@ -964,8 +964,8 @@ impl RouteSpecStore { let safety_spec = SafetySpec { preferred_route: None, hop_count: self.unlocked_inner.default_route_hop_count, - stability: Stability::LowLatency, - sequencing: Sequencing::NoPreference, + stability: Stability::default(), + sequencing: Sequencing::default(), }; let safety_selection = SafetySelection::Safe(safety_spec); diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 4119e3ef..93d4beaf 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -57,14 +57,14 @@ fn get_safety_selection(text: &str, rss: RouteSpecStore) -> Option impl FnOnce(&str) -> Option { return Err(VeilidAPIError::parse_error($x, $y)) }; @@ -563,6 +563,12 @@ pub enum Sequencing { EnsureOrdered, } +impl Default for Sequencing { + fn default() -> Self { + Self::NoPreference + } +} + // Ordering here matters, >= is used to check strength of stability requirement #[derive( Copy, @@ -585,6 +591,12 @@ pub enum Stability { Reliable, } +impl Default for Stability { + fn default() -> Self { + Self::LowLatency + } +} + /// The choice of safety route to include in compiled routes #[derive( Copy, @@ -1543,10 +1555,7 @@ impl FromStr for DialInfo { VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url) })?; if split_url.scheme != "ws" || !url.starts_with("ws://") { - return Err(VeilidAPIError::parse_error( - "incorrect scheme for WS dialinfo", - url, - )); + apibail_parse_error!("incorrect scheme for WS dialinfo", url); } let url_port = split_url.port.unwrap_or(80u16); @@ -1574,10 +1583,7 @@ impl FromStr for DialInfo { VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url) })?; if split_url.scheme != "wss" || !url.starts_with("wss://") { - return Err(VeilidAPIError::parse_error( - "incorrect scheme for WSS dialinfo", - url, - )); + apibail_parse_error!("incorrect scheme for WSS dialinfo", url); } let url_port = split_url.port.unwrap_or(443u16); @@ -1628,24 +1634,18 @@ impl DialInfo { VeilidAPIError::parse_error(format!("unable to split WS url: {}", e), &url) })?; if split_url.scheme != "ws" || !url.starts_with("ws://") { - return Err(VeilidAPIError::parse_error( - "incorrect scheme for WS dialinfo", - url, - )); + apibail_parse_error!("incorrect scheme for WS dialinfo", url); } let url_port = split_url.port.unwrap_or(80u16); if url_port != socket_address.port() { - return Err(VeilidAPIError::parse_error( - "socket address port doesn't match url port", - url, - )); + apibail_parse_error!("socket address port doesn't match url port", url); } if let SplitUrlHost::IpAddr(a) = split_url.host { if socket_address.to_ip_addr() != a { - return Err(VeilidAPIError::parse_error( + apibail_parse_error!( format!("request address does not match socket address: {}", a), - socket_address, - )); + socket_address + ); } } Ok(Self::WS(DialInfoWS { @@ -1658,23 +1658,17 @@ impl DialInfo { VeilidAPIError::parse_error(format!("unable to split WSS url: {}", e), &url) })?; if split_url.scheme != "wss" || !url.starts_with("wss://") { - return Err(VeilidAPIError::parse_error( - "incorrect scheme for WSS dialinfo", - url, - )); + apibail_parse_error!("incorrect scheme for WSS dialinfo", url); } let url_port = split_url.port.unwrap_or(443u16); if url_port != socket_address.port() { - return Err(VeilidAPIError::parse_error( - "socket address port doesn't match url port", - url, - )); + apibail_parse_error!("socket address port doesn't match url port", url); } if !matches!(split_url.host, SplitUrlHost::Hostname(_)) { - return Err(VeilidAPIError::parse_error( + apibail_parse_error!( "WSS url can not use address format, only hostname format", - url, - )); + url + ); } Ok(Self::WSS(DialInfoWSS { socket_address: socket_address.to_canonical(), @@ -1778,10 +1772,7 @@ impl DialInfo { let hostname = hostname.as_ref(); if short.len() < 2 { - return Err(VeilidAPIError::parse_error( - "invalid short url length", - short, - )); + apibail_parse_error!("invalid short url length", short); } let url = match &short[0..1] { "U" => { @@ -1797,7 +1788,7 @@ impl DialInfo { format!("wss://{}:{}", hostname, &short[1..]) } _ => { - return Err(VeilidAPIError::parse_error("invalid short url type", short)); + apibail_parse_error!("invalid short url type", short); } }; Self::try_vec_from_url(url) @@ -1815,10 +1806,7 @@ impl DialInfo { "ws" => split_url.port.unwrap_or(80u16), "wss" => split_url.port.unwrap_or(443u16), _ => { - return Err(VeilidAPIError::parse_error( - "Invalid dial info url scheme", - split_url.scheme, - )); + apibail_parse_error!("Invalid dial info url scheme", split_url.scheme); } }; @@ -2753,36 +2741,35 @@ impl VeilidAPI { // Private route allocation #[instrument(level = "debug", skip(self))] - pub async fn new_default_private_route(&self) -> Result<(DHTKey, Vec), VeilidAPIError> { - let config = self.config()?; - let c = config.get(); - self.new_private_route( - Stability::LowLatency, - Sequencing::NoPreference, - c.network.rpc.default_route_hop_count.into(), - ) - .await + pub async fn new_private_route(&self) -> Result<(DHTKey, Vec), VeilidAPIError> { + self.new_custom_private_route(Stability::default(), Sequencing::default()) + .await } #[instrument(level = "debug", skip(self))] - pub async fn new_private_route( + pub async fn new_custom_private_route( &self, stability: Stability, sequencing: Sequencing, - hop_count: usize, ) -> Result<(DHTKey, Vec), VeilidAPIError> { + let default_route_hop_count: usize = { + let config = self.config()?; + let c = config.get(); + c.network.rpc.default_route_hop_count.into() + }; + let rss = self.routing_table()?.route_spec_store(); let r = rss .allocate_route( stability, sequencing, - hop_count, + default_route_hop_count, Direction::Inbound.into(), &[], ) .map_err(VeilidAPIError::internal)?; let Some(pr_pubkey) = r else { - return Err(VeilidAPIError::generic("unable to allocate route")); + apibail_generic!("unable to allocate route"); }; if !rss .test_route(&pr_pubkey) @@ -2790,7 +2777,7 @@ impl VeilidAPI { .map_err(VeilidAPIError::no_connection)? { rss.release_route(&pr_pubkey); - return Err(VeilidAPIError::generic("allocated route failed to test")); + apibail_generic!("allocated route failed to test"); } let private_route = rss .assemble_private_route(&pr_pubkey, Some(true)) @@ -2799,12 +2786,37 @@ impl VeilidAPI { Ok(v) => v, Err(e) => { rss.release_route(&pr_pubkey); - return Err(VeilidAPIError::internal(e)); + apibail_internal!(e); } }; + + rss.mark_route_published(&pr_pubkey, true) + .map_err(VeilidAPIError::internal)?; + Ok((pr_pubkey, blob)) } + #[instrument(level = "debug", skip(self))] + pub fn import_remote_private_route(&self, blob: Vec) -> Result { + let rss = self.routing_table()?.route_spec_store(); + rss.import_remote_private_route(blob) + .map_err(|e| VeilidAPIError::invalid_argument(e, "blob", "private route blob")) + } + + #[instrument(level = "debug", skip(self))] + pub fn release_private_route(&self, key: &DHTKey) -> Result<(), VeilidAPIError> { + let rss = self.routing_table()?.route_spec_store(); + if rss.release_route(key) { + Ok(()) + } else { + Err(VeilidAPIError::invalid_argument( + "release_private_route", + "key", + key, + )) + } + } + //////////////////////////////////////////////////////////////// // App Calls diff --git a/veilid-core/src/veilid_api/routing_context.rs b/veilid-core/src/veilid_api/routing_context.rs index f33ce133..3af6eaae 100644 --- a/veilid-core/src/veilid_api/routing_context.rs +++ b/veilid-core/src/veilid_api/routing_context.rs @@ -39,14 +39,19 @@ impl RoutingContext { api, inner: Arc::new(Mutex::new(RoutingContextInner {})), unlocked_inner: Arc::new(RoutingContextUnlockedInner { - safety_selection: SafetySelection::Unsafe(Sequencing::NoPreference), + safety_selection: SafetySelection::Unsafe(Sequencing::default()), }), } } - pub fn with_default_privacy(self) -> Result { + pub fn with_privacy(self) -> Result { + self.with_custom_privacy(Stability::default()) + } + + pub fn with_custom_privacy(self, stability: Stability) -> Result { let config = self.api.config()?; let c = config.get(); + Ok(Self { api: self.api.clone(), inner: Arc::new(Mutex::new(RoutingContextInner {})), @@ -54,22 +59,13 @@ impl RoutingContext { safety_selection: SafetySelection::Safe(SafetySpec { preferred_route: None, hop_count: c.network.rpc.default_route_hop_count as usize, - stability: Stability::LowLatency, - sequencing: Sequencing::NoPreference, + stability, + sequencing: self.sequencing(), }), }), }) } - pub fn with_privacy(self, safety_spec: SafetySpec) -> Result { - Ok(Self { - api: self.api.clone(), - inner: Arc::new(Mutex::new(RoutingContextInner {})), - unlocked_inner: Arc::new(RoutingContextUnlockedInner { - safety_selection: SafetySelection::Safe(safety_spec), - }), - }) - } - + pub fn with_sequencing(self, sequencing: Sequencing) -> Self { Self { api: self.api.clone(), @@ -87,18 +83,13 @@ impl RoutingContext { }), } } - pub fn sequencing(&self) -> Sequencing { + + fn sequencing(&self) -> Sequencing { match self.unlocked_inner.safety_selection { SafetySelection::Unsafe(sequencing) => sequencing, SafetySelection::Safe(safety_spec) => safety_spec.sequencing, } } - pub fn safety_spec(&self) -> Option { - match self.unlocked_inner.safety_selection { - SafetySelection::Unsafe(_) => None, - SafetySelection::Safe(safety_spec) => Some(safety_spec.clone()), - } - } pub fn api(&self) -> VeilidAPI { self.api.clone() diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 79c37d22..3a33fc0e 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -756,7 +756,7 @@ impl VeilidConfig { let mut out = &jvc; for k in keypath { if !out.has_key(k) { - apibail_parse!(format!("invalid subkey in key '{}'", key), k); + apibail_parse_error!(format!("invalid subkey in key '{}'", key), k); } out = &out[k]; } @@ -781,12 +781,12 @@ impl VeilidConfig { let mut out = &mut jvc; for k in objkeypath { if !out.has_key(*k) { - apibail_parse!(format!("invalid subkey in key '{}'", key), k); + apibail_parse_error!(format!("invalid subkey in key '{}'", key), k); } out = &mut out[*k]; } if !out.has_key(objkeyname) { - apibail_parse!(format!("invalid subkey in key '{}'", key), objkeyname); + apibail_parse_error!(format!("invalid subkey in key '{}'", key), objkeyname); } out[*objkeyname] = newval; jvc.to_string() diff --git a/veilid-core/src/xx/ip_extra.rs b/veilid-core/src/xx/ip_extra.rs index 8899c719..5328359d 100644 --- a/veilid-core/src/xx/ip_extra.rs +++ b/veilid-core/src/xx/ip_extra.rs @@ -1,6 +1,5 @@ // // This file really shouldn't be necessary, but 'ip' isn't a stable feature -// and things may not agree between the no_std_net crate and the stuff in std. // use crate::xx::*; diff --git a/veilid-core/src/xx/mod.rs b/veilid-core/src/xx/mod.rs index a4c8e008..4169be90 100644 --- a/veilid-core/src/xx/mod.rs +++ b/veilid-core/src/xx/mod.rs @@ -55,6 +55,9 @@ pub use std::convert::{TryFrom, TryInto}; pub use std::fmt; pub use std::future::Future; pub use std::mem; +pub use std::net::{ + IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, +}; pub use std::ops::{Fn, FnMut, FnOnce}; pub use std::pin::Pin; pub use std::rc::Rc; @@ -73,10 +76,7 @@ cfg_if! { pub use async_lock::MutexGuard as AsyncMutexGuard; pub use async_lock::MutexGuardArc as AsyncMutexGuardArc; pub use async_executors::JoinHandle as LowLevelJoinHandle; - - pub use no_std_net::{ SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, IpAddr, Ipv4Addr, Ipv6Addr }; } else { - cfg_if! { if #[cfg(feature="rt-async-std")] { pub use async_std::sync::Mutex as AsyncMutex; @@ -92,7 +92,6 @@ cfg_if! { #[compile_error("must use an executor")] } } - pub use std::net::{ SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, IpAddr, Ipv4Addr, Ipv6Addr }; } } diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 77c26268..196df95d 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -1696,6 +1696,70 @@ class VeilidVersion { VeilidVersion(this.major, this.minor, this.patch); } +////////////////////////////////////// +/// Stability + +enum Stability { + lowLatency, + reliable, +} + +extension StabilityExt on Stability { + String get json { + return name.toPascalCase(); + } +} + +Stability stabilityFromJson(String j) { + return Stability.values.byName(j.toCamelCase()); +} + +////////////////////////////////////// +/// Sequencing + +enum Sequencing { + noPreference, + preferOrdered, + ensureOrdered, +} + +extension SequencingExt on Sequencing { + String get json { + return name.toPascalCase(); + } +} + +Sequencing sequencingFromJson(String j) { + return Sequencing.values.byName(j.toCamelCase()); +} + +////////////////////////////////////// +/// KeyBlob +class KeyBlob { + final String key; + final Uint8List blob; + + KeyBlob(this.key, this.blob); + + KeyBlob.fromJson(Map json) + : key = json['key'], + blob = base64Decode(json['blob']); + + Map get json { + return {'key': key, 'blob': base64UrlEncode(blob)}; + } +} + +////////////////////////////////////// +/// VeilidRoutingContext +abstract class VeilidRoutingContext { + VeilidRoutingContext withPrivacy(); + VeilidRoutingContext withCustomPrivacy(Stability stability); + VeilidRoutingContext withSequencing(Sequencing sequencing); + Future appCall(String target, Uint8List request); + Future appMessage(String target, Uint8List message); +} + ////////////////////////////////////// /// Veilid singleton factory @@ -1709,8 +1773,22 @@ abstract class Veilid { Future attach(); Future detach(); Future shutdownVeilidCore(); - Future debug(String command); + + // Routing context + Future routingContext(); + + // Private route allocation + Future newPrivateRoute(); + Future newCustomPrivateRoute( + Stability stability, Sequencing sequencing); + Future importRemotePrivateRoute(Uint8List blob); + Future releasePrivateRoute(String key); + + // App calls Future appCallReply(String id, Uint8List message); + + // Misc String veilidVersionString(); VeilidVersion veilidVersion(); + Future debug(String command); } diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index f4b87f41..63ca4eea 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -48,13 +48,56 @@ typedef _AttachDart = void Function(int); // fn detach(port: i64) typedef _DetachC = Void Function(Int64); typedef _DetachDart = void Function(int); -// fn debug(port: i64, log_level: FfiStr) -typedef _DebugC = Void Function(Int64, Pointer); -typedef _DebugDart = void Function(int, Pointer); + +// fn routing_context(port: i64) +typedef _RoutingContextC = Void Function(Int64); +typedef _RoutingContextDart = void Function(int); +// fn release_routing_context(id: u32) +typedef _ReleaseRoutingContextC = Int32 Function(Uint32); +typedef _ReleaseRoutingContextDart = int Function(int); +// fn routing_context_with_privacy(id: u32) -> u32 +typedef _RoutingContextWithPrivacyC = Uint32 Function(Uint32); +typedef _RoutingContextWithPrivacyDart = int Function(int); +// fn routing_context_with_custom_privacy(id: u32, stability: FfiStr) +typedef _RoutingContextWithCustomPrivacyC = Uint32 Function( + Uint32, Pointer); +typedef _RoutingContextWithCustomPrivacyDart = int Function(int, Pointer); +// fn routing_context_with_sequencing(id: u32, sequencing: FfiStr) +typedef _RoutingContextWithSequencingC = Uint32 Function(Uint32, Pointer); +typedef _RoutingContextWithSequencingDart = int Function(int, Pointer); +// fn routing_context_app_call(port: i64, id: u32, target: FfiStr, request: FfiStr) +typedef _RoutingContextAppCallC = Void Function( + Int64, Uint32, Pointer, Pointer); +typedef _RoutingContextAppCallDart = void Function( + int, int, Pointer, Pointer); +// fn routing_context_app_message(port: i64, id: u32, target: FfiStr, request: FfiStr) +typedef _RoutingContextAppMessageC = Void Function( + Int64, Uint32, Pointer, Pointer); +typedef _RoutingContextAppMessageDart = void Function( + int, int, Pointer, Pointer); + +// fn new_private_route(port: i64) +typedef _NewPrivateRouteC = Void Function(Int64); +typedef _NewPrivateRouteDart = void Function(int); +// fn new_custom_private_route(port: i64, stability: FfiStr, sequencing: FfiStr) +typedef _NewCustomPrivateRouteC = Void Function( + Int64, Pointer, Pointer); +typedef _NewCustomPrivateRouteDart = void Function( + int, Pointer, Pointer); +// fn import_remote_private_route(port: i64, blob: FfiStr) +typedef _ImportRemotePrivateRouteC = Void Function(Int64, Pointer); +typedef _ImportRemotePrivateRouteDart = void Function(int, Pointer); +// fn release_private_route(port:i64, key: FfiStr) +typedef _ReleasePrivateRouteC = Void Function(Int64, Pointer); +typedef _ReleasePrivateRouteDart = void Function(int, Pointer); + // fn app_call_reply(port: i64, id: FfiStr, message: FfiStr) typedef _AppCallReplyC = Void Function(Int64, Pointer, Pointer); typedef _AppCallReplyDart = void Function(int, Pointer, Pointer); +// fn debug(port: i64, log_level: FfiStr) +typedef _DebugC = Void Function(Int64, Pointer); +typedef _DebugDart = void Function(int, Pointer); // fn shutdown_veilid_core(port: i64) typedef _ShutdownVeilidCoreC = Void Function(Int64); typedef _ShutdownVeilidCoreDart = void Function(int); @@ -294,6 +337,58 @@ Stream processStreamJson( } } +// FFI implementation of VeilidRoutingContext +class VeilidRoutingContextFFI implements VeilidRoutingContext { + final int _id; + final VeilidFFI _ffi; + + VeilidRoutingContextFFI._(this._id, this._ffi); + @override + VeilidRoutingContextFFI withPrivacy() { + final newId = _ffi._routingContextWithPrivacy(_id); + return VeilidRoutingContextFFI._(newId, _ffi); + } + + @override + VeilidRoutingContextFFI withCustomPrivacy(Stability stability) { + final newId = _ffi._routingContextWithCustomPrivacy( + _id, stability.json.toNativeUtf8()); + return VeilidRoutingContextFFI._(newId, _ffi); + } + + @override + VeilidRoutingContextFFI withSequencing(Sequencing sequencing) { + final newId = + _ffi._routingContextWithSequencing(_id, sequencing.json.toNativeUtf8()); + return VeilidRoutingContextFFI._(newId, _ffi); + } + + @override + Future appCall(String target, Uint8List request) async { + var nativeEncodedTarget = target.toNativeUtf8(); + var nativeEncodedRequest = base64UrlEncode(request).toNativeUtf8(); + + final recvPort = ReceivePort("routing_context_app_call"); + final sendPort = recvPort.sendPort; + _ffi._routingContextAppCall( + sendPort.nativePort, _id, nativeEncodedTarget, nativeEncodedRequest); + final out = await processFuturePlain(recvPort.first); + return base64Decode(out); + } + + @override + Future appMessage(String target, Uint8List message) async { + var nativeEncodedTarget = target.toNativeUtf8(); + var nativeEncodedMessage = base64UrlEncode(message).toNativeUtf8(); + + final recvPort = ReceivePort("routing_context_app_call"); + final sendPort = recvPort.sendPort; + _ffi._routingContextAppCall( + sendPort.nativePort, _id, nativeEncodedTarget, nativeEncodedMessage); + return processFutureVoid(recvPort.first); + } +} + // FFI implementation of high level Veilid API class VeilidFFI implements Veilid { // veilid_core shared library @@ -308,8 +403,23 @@ class VeilidFFI implements Veilid { final _AttachDart _attach; final _DetachDart _detach; final _ShutdownVeilidCoreDart _shutdownVeilidCore; - final _DebugDart _debug; + + final _RoutingContextDart _routingContext; + final _ReleaseRoutingContextDart _releaseRoutingContext; + final _RoutingContextWithPrivacyDart _routingContextWithPrivacy; + final _RoutingContextWithCustomPrivacyDart _routingContextWithCustomPrivacy; + final _RoutingContextWithSequencingDart _routingContextWithSequencing; + final _RoutingContextAppCallDart _routingContextAppCall; + final _RoutingContextAppMessageDart _routingContextAppMessage; + + final _NewPrivateRouteDart _newPrivateRoute; + final _NewCustomPrivateRouteDart _newCustomPrivateRoute; + final _ImportRemotePrivateRouteDart _importRemotePrivateRoute; + final _ReleasePrivateRouteDart _releasePrivateRoute; + final _AppCallReplyDart _appCallReply; + + final _DebugDart _debug; final _VeilidVersionStringDart _veilidVersionString; final _VeilidVersionDart _veilidVersion; @@ -333,9 +443,40 @@ class VeilidFFI implements Veilid { _shutdownVeilidCore = dylib.lookupFunction<_ShutdownVeilidCoreC, _ShutdownVeilidCoreDart>( 'shutdown_veilid_core'), - _debug = dylib.lookupFunction<_DebugC, _DebugDart>('debug'), + _routingContext = + dylib.lookupFunction<_RoutingContextC, _RoutingContextDart>( + 'routing_context'), + _releaseRoutingContext = dylib.lookupFunction<_ReleaseRoutingContextC, + _ReleaseRoutingContextDart>('release_routing_context'), + _routingContextWithPrivacy = dylib.lookupFunction< + _RoutingContextWithPrivacyC, + _RoutingContextWithPrivacyDart>('routing_context_with_privacy'), + _routingContextWithCustomPrivacy = dylib.lookupFunction< + _RoutingContextWithCustomPrivacyC, + _RoutingContextWithCustomPrivacyDart>( + 'routing_context_with_custom_privacy'), + _routingContextWithSequencing = dylib.lookupFunction< + _RoutingContextWithSequencingC, + _RoutingContextWithSequencingDart>( + 'routing_context_with_sequencing'), + _routingContextAppCall = dylib.lookupFunction<_RoutingContextAppCallC, + _RoutingContextAppCallDart>('routing_context_app_call'), + _routingContextAppMessage = dylib.lookupFunction< + _RoutingContextAppMessageC, + _RoutingContextAppMessageDart>('routing_context_app_message'), + _newPrivateRoute = + dylib.lookupFunction<_NewPrivateRouteC, _NewPrivateRouteDart>( + 'new_private_route'), + _newCustomPrivateRoute = dylib.lookupFunction<_NewCustomPrivateRouteC, + _NewCustomPrivateRouteDart>('new_custom_private_route'), + _importRemotePrivateRoute = dylib.lookupFunction< + _ImportRemotePrivateRouteC, + _ImportRemotePrivateRouteDart>('import_remote_private_route'), + _releasePrivateRoute = dylib.lookupFunction<_ReleasePrivateRouteC, + _ReleasePrivateRouteDart>('release_private_route'), _appCallReply = dylib.lookupFunction<_AppCallReplyC, _AppCallReplyDart>( 'app_call_reply'), + _debug = dylib.lookupFunction<_DebugC, _DebugDart>('debug'), _veilidVersionString = dylib.lookupFunction<_VeilidVersionStringC, _VeilidVersionStringDart>('veilid_version_string'), _veilidVersion = @@ -420,14 +561,53 @@ class VeilidFFI implements Veilid { } @override - Future debug(String command) async { - var nativeCommand = command.toNativeUtf8(); - final recvPort = ReceivePort("debug"); + Future routingContext() async { + final recvPort = ReceivePort("routing_context"); final sendPort = recvPort.sendPort; - _debug(sendPort.nativePort, nativeCommand); + _routingContext(sendPort.nativePort); + final id = await processFuturePlain(recvPort.first); + return VeilidRoutingContextFFI._(id, this); + } + + @override + Future newPrivateRoute() async { + final recvPort = ReceivePort("new_private_route"); + final sendPort = recvPort.sendPort; + _newPrivateRoute(sendPort.nativePort); + return processFutureJson(KeyBlob.fromJson, recvPort.first); + } + + @override + Future newCustomPrivateRoute( + Stability stability, Sequencing sequencing) async { + final recvPort = ReceivePort("new_custom_private_route"); + final sendPort = recvPort.sendPort; + _newCustomPrivateRoute(sendPort.nativePort, stability.json.toNativeUtf8(), + sequencing.json.toNativeUtf8()); + final keyblob = await processFutureJson(KeyBlob.fromJson, recvPort.first); + return keyblob; + } + + @override + Future importRemotePrivateRoute(Uint8List blob) async { + var nativeEncodedBlob = base64UrlEncode(blob).toNativeUtf8(); + + final recvPort = ReceivePort("import_remote_private_route"); + final sendPort = recvPort.sendPort; + _importRemotePrivateRoute(sendPort.nativePort, nativeEncodedBlob); return processFuturePlain(recvPort.first); } + @override + Future releasePrivateRoute(String key) async { + var nativeEncodedKey = key.toNativeUtf8(); + + final recvPort = ReceivePort("release_private_route"); + final sendPort = recvPort.sendPort; + _releasePrivateRoute(sendPort.nativePort, nativeEncodedKey); + return processFutureVoid(recvPort.first); + } + @override Future appCallReply(String id, Uint8List message) async { var nativeId = id.toNativeUtf8(); @@ -438,6 +618,15 @@ class VeilidFFI implements Veilid { return processFutureVoid(recvPort.first); } + @override + Future debug(String command) async { + var nativeCommand = command.toNativeUtf8(); + final recvPort = ReceivePort("debug"); + final sendPort = recvPort.sendPort; + _debug(sendPort.nativePort, nativeCommand); + return processFuturePlain(recvPort.first); + } + @override String veilidVersionString() { final versionString = _veilidVersionString(); diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart index 1d5e907c..7353ae28 100644 --- a/veilid-flutter/lib/veilid_js.dart +++ b/veilid-flutter/lib/veilid_js.dart @@ -19,6 +19,61 @@ Future _wrapApiPromise(Object p) { VeilidAPIException.fromJson(jsonDecode(error as String)))); } +// JS implementation of VeilidRoutingContext +class VeilidRoutingContextJS implements VeilidRoutingContext { + final int _id; + final VeilidFFI _ffi; + + VeilidRoutingContextFFI._(this._id, this._ffi); + @override + VeilidRoutingContextFFI withPrivacy() { + final newId = _ffi._routingContextWithPrivacy(_id); + return VeilidRoutingContextFFI._(newId, _ffi); + } + + @override + VeilidRoutingContextFFI withCustomPrivacy(Stability stability) { + final newId = _ffi._routingContextWithCustomPrivacy( + _id, stability.json.toNativeUtf8()); + return VeilidRoutingContextFFI._(newId, _ffi); + } + + @override + VeilidRoutingContextFFI withSequencing(Sequencing sequencing) { + final newId = + _ffi._routingContextWithSequencing(_id, sequencing.json.toNativeUtf8()); + return VeilidRoutingContextFFI._(newId, _ffi); + } + + @override + Future appCall(String target, Uint8List request) async { + var nativeEncodedTarget = target.toNativeUtf8(); + var nativeEncodedRequest = base64UrlEncode(request).toNativeUtf8(); + + final recvPort = ReceivePort("routing_context_app_call"); + final sendPort = recvPort.sendPort; + _ffi._routingContextAppCall( + sendPort.nativePort, _id, nativeEncodedTarget, nativeEncodedRequest); + final out = await processFuturePlain(recvPort.first); + return base64Decode(out); + } + + @override + Future appMessage(String target, Uint8List message) async { + var nativeEncodedTarget = target.toNativeUtf8(); + var nativeEncodedMessage = base64UrlEncode(message).toNativeUtf8(); + + final recvPort = ReceivePort("routing_context_app_call"); + final sendPort = recvPort.sendPort; + _ffi._routingContextAppCall( + sendPort.nativePort, _id, nativeEncodedTarget, nativeEncodedMessage); + return processFutureVoid(recvPort.first); + } +} + + +// JS implementation of high level Veilid API + class VeilidJS implements Veilid { @override void initializeVeilidCore(Map platformConfigJson) { @@ -78,9 +133,43 @@ class VeilidJS implements Veilid { js_util.callMethod(wasm, "shutdown_veilid_core", [])); } + @override - Future debug(String command) { - return _wrapApiPromise(js_util.callMethod(wasm, "debug", [command])); + Future routingContext() async { + final recvPort = ReceivePort("routing_context"); + final sendPort = recvPort.sendPort; + _routingContext(sendPort.nativePort); + final id = await processFuturePlain(recvPort.first); + return VeilidRoutingContextFFI._(id, this); + } + + @override + Future newPrivateRoute() async { + final recvPort = ReceivePort("new_private_route"); + final sendPort = recvPort.sendPort; + _newPrivateRoute(sendPort.nativePort); + return processFutureJson(KeyBlob.fromJson, recvPort.first); + } + + @override + Future newCustomPrivateRoute( + Stability stability, Sequencing sequencing) async { + return _wrapApiPromise( + js_util.callMethod(wasm, "new_custom_private_route", [stability, sequencing])); + + } + + @override + Future importRemotePrivateRoute(Uint8List blob) async { + var encodedBlob = base64UrlEncode(blob); + return _wrapApiPromise( + js_util.callMethod(wasm, "import_remote_private_route", [encodedBlob])); + } + + @override + Future releasePrivateRoute(String key) async { + return _wrapApiPromise( + js_util.callMethod(wasm, "release_private_route", [key])); } @override @@ -89,6 +178,11 @@ class VeilidJS implements Veilid { return _wrapApiPromise( js_util.callMethod(wasm, "app_call_reply", [id, encodedMessage])); } + + @override + Future debug(String command) { + return _wrapApiPromise(js_util.callMethod(wasm, "debug", [command])); + } @override String veilidVersionString() { diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 00080298..2a45485e 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -21,6 +21,8 @@ lazy_static! { static ref VEILID_API: AsyncMutex> = AsyncMutex::new(None); static ref FILTERS: Mutex> = Mutex::new(BTreeMap::new()); + static ref ROUTING_CONTEXTS: Mutex> = + Mutex::new(BTreeMap::new()); } async fn get_veilid_api() -> Result { @@ -49,7 +51,7 @@ type APIResult = Result; const APIRESULT_VOID: APIResult<()> = APIResult::Ok(()); ///////////////////////////////////////// -// FFI-specific cofnig +// FFI-specific #[derive(Debug, Deserialize, Serialize)] pub struct VeilidFFIConfigLoggingTerminal { @@ -83,6 +85,13 @@ pub struct VeilidFFIConfig { pub logging: VeilidFFIConfigLogging, } +#[derive(Debug, Deserialize, Serialize)] +pub struct VeilidFFIKeyBlob { + pub key: veilid_core::DHTKey, + #[serde(with = "veilid_core::json_as_base64")] + pub blob: Vec, +} + ///////////////////////////////////////// // Initializer #[no_mangle] @@ -317,13 +326,208 @@ pub extern "C" fn shutdown_veilid_core(port: i64) { }); } +fn add_routing_context(routing_context: veilid_core::RoutingContext) -> u32 { + let mut next_id: u32 = 1; + let mut rc = ROUTING_CONTEXTS.lock(); + while rc.contains_key(&next_id) { + next_id += 1; + } + rc.insert(next_id, routing_context); + next_id +} + #[no_mangle] -pub extern "C" fn debug(port: i64, command: FfiStr) { - let command = command.into_opt_string().unwrap_or_default(); +pub extern "C" fn routing_context(port: i64) { DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; - let out = veilid_api.debug(command).await?; - APIResult::Ok(out) + let routing_context = veilid_api.routing_context(); + let new_id = add_routing_context(routing_context); + APIResult::Ok(new_id) + }); +} + +#[no_mangle] +pub extern "C" fn release_routing_context(id: u32) -> i32 { + let mut rc = ROUTING_CONTEXTS.lock(); + if rc.remove(&id).is_none() { + return 0; + } + return 1; +} + +#[no_mangle] +pub extern "C" fn routing_context_with_privacy(id: u32) -> u32 { + let rc = ROUTING_CONTEXTS.lock(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + let Ok(routing_context) = routing_context.clone().with_privacy() else { + return 0; + }; + let new_id = add_routing_context(routing_context); + new_id +} + +#[no_mangle] +pub extern "C" fn routing_context_with_custom_privacy(id: u32, stability: FfiStr) -> u32 { + let stability: veilid_core::Stability = + veilid_core::deserialize_opt_json(stability.into_opt_string()).unwrap(); + + let rc = ROUTING_CONTEXTS.lock(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + let Ok(routing_context) = routing_context.clone().with_custom_privacy(stability) else { + return 0; + }; + let new_id = add_routing_context(routing_context); + new_id +} + +#[no_mangle] +pub extern "C" fn routing_context_with_sequencing(id: u32, sequencing: FfiStr) -> u32 { + let sequencing: veilid_core::Sequencing = + veilid_core::deserialize_opt_json(sequencing.into_opt_string()).unwrap(); + + let rc = ROUTING_CONTEXTS.lock(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + let routing_context = routing_context.clone().with_sequencing(sequencing); + let new_id = add_routing_context(routing_context); + new_id +} + +#[no_mangle] +pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, request: FfiStr) { + let target: veilid_core::DHTKey = + veilid_core::deserialize_opt_json(target.into_opt_string()).unwrap(); + let request: Vec = data_encoding::BASE64URL_NOPAD + .decode( + veilid_core::deserialize_opt_json::(request.into_opt_string()) + .unwrap() + .as_bytes(), + ) + .unwrap(); + DartIsolateWrapper::new(port).spawn_result_json(async move { + let veilid_api = get_veilid_api().await?; + let routing_table = veilid_api.routing_table()?; + let rss = routing_table.route_spec_store(); + + let routing_context = { + let rc = ROUTING_CONTEXTS.lock(); + let Some(routing_context) = rc.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_call", "id", id)); + }; + routing_context.clone() + }; + + let target = if rss.get_remote_private_route(&target).is_some() { + veilid_core::Target::PrivateRoute(target) + } else { + veilid_core::Target::NodeId(veilid_core::NodeId::new(target)) + }; + + let answer = routing_context.app_call(target, request).await?; + let answer = data_encoding::BASE64URL_NOPAD.encode(&answer); + APIResult::Ok(answer) + }); +} + +#[no_mangle] +pub extern "C" fn routing_context_app_message(port: i64, id: u32, target: FfiStr, message: FfiStr) { + let target: veilid_core::DHTKey = + veilid_core::deserialize_opt_json(target.into_opt_string()).unwrap(); + let message: Vec = data_encoding::BASE64URL_NOPAD + .decode( + veilid_core::deserialize_opt_json::(message.into_opt_string()) + .unwrap() + .as_bytes(), + ) + .unwrap(); + DartIsolateWrapper::new(port).spawn_result_json(async move { + let veilid_api = get_veilid_api().await?; + let routing_table = veilid_api.routing_table()?; + let rss = routing_table.route_spec_store(); + + let routing_context = { + let rc = ROUTING_CONTEXTS.lock(); + let Some(routing_context) = rc.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_call", "id", id)); + }; + routing_context.clone() + }; + + let target = if rss.get_remote_private_route(&target).is_some() { + veilid_core::Target::PrivateRoute(target) + } else { + veilid_core::Target::NodeId(veilid_core::NodeId::new(target)) + }; + + routing_context.app_message(target, message).await?; + APIRESULT_VOID + }); +} + +#[no_mangle] +pub extern "C" fn new_private_route(port: i64) { + DartIsolateWrapper::new(port).spawn_result_json(async move { + let veilid_api = get_veilid_api().await?; + + let (key, blob) = veilid_api.new_private_route().await?; + + let keyblob = VeilidFFIKeyBlob { key, blob }; + + APIResult::Ok(keyblob) + }); +} + +#[no_mangle] +pub extern "C" fn new_custom_private_route(port: i64, stability: FfiStr, sequencing: FfiStr) { + let stability: veilid_core::Stability = + veilid_core::deserialize_opt_json(stability.into_opt_string()).unwrap(); + let sequencing: veilid_core::Sequencing = + veilid_core::deserialize_opt_json(sequencing.into_opt_string()).unwrap(); + + DartIsolateWrapper::new(port).spawn_result_json(async move { + let veilid_api = get_veilid_api().await?; + + let (key, blob) = veilid_api + .new_custom_private_route(stability, sequencing) + .await?; + + let keyblob = VeilidFFIKeyBlob { key, blob }; + + APIResult::Ok(keyblob) + }); +} + +#[no_mangle] +pub extern "C" fn import_remote_private_route(port: i64, blob: FfiStr) { + let blob: Vec = data_encoding::BASE64URL_NOPAD + .decode( + veilid_core::deserialize_opt_json::(blob.into_opt_string()) + .unwrap() + .as_bytes(), + ) + .unwrap(); + DartIsolateWrapper::new(port).spawn_result(async move { + let veilid_api = get_veilid_api().await?; + + let key = veilid_api.import_remote_private_route(blob)?; + + APIResult::Ok(key.encode()) + }); +} + +#[no_mangle] +pub extern "C" fn release_private_route(port: i64, key: FfiStr) { + let key: veilid_core::DHTKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + DartIsolateWrapper::new(port).spawn_result_json(async move { + let veilid_api = get_veilid_api().await?; + veilid_api.release_private_route(&key)?; + APIRESULT_VOID }); } @@ -347,6 +551,16 @@ pub extern "C" fn app_call_reply(port: i64, id: FfiStr, message: FfiStr) { }); } +#[no_mangle] +pub extern "C" fn debug(port: i64, command: FfiStr) { + let command = command.into_opt_string().unwrap_or_default(); + DartIsolateWrapper::new(port).spawn_result(async move { + let veilid_api = get_veilid_api().await?; + let out = veilid_api.debug(command).await?; + APIResult::Ok(out) + }); +} + #[no_mangle] pub extern "C" fn veilid_version_string() -> *mut c_char { veilid_core::veilid_version_string().into_ffi_value() diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index e49bb8e6..3fd7e891 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -39,6 +39,8 @@ lazy_static! { SendWrapper::new(RefCell::new(None)); static ref FILTERS: SendWrapper>> = SendWrapper::new(RefCell::new(BTreeMap::new())); + static ref ROUTING_CONTEXTS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); } fn get_veilid_api() -> Result { @@ -54,20 +56,7 @@ fn take_veilid_api() -> Result(val: T) -> String { - serde_json::to_string(&val).expect("failed to serialize json value") -} - -pub fn deserialize_json( - arg: &str, -) -> Result { - serde_json::from_str(arg).map_err(|e| veilid_core::VeilidAPIError::ParseError { - message: e.to_string(), - value: String::new(), - }) -} - +// JSON Helpers for WASM pub fn to_json(val: T) -> JsValue { JsValue::from_str(&serialize_json(val)) } @@ -104,7 +93,7 @@ where } ///////////////////////////////////////// -// WASM-specific cofnig +// WASM-specific #[derive(Debug, Deserialize, Serialize)] pub struct VeilidWASMConfigLoggingPerformance { @@ -131,6 +120,13 @@ pub struct VeilidWASMConfig { pub logging: VeilidWASMConfigLogging, } +#[derive(Debug, Deserialize, Serialize)] +pub struct VeilidFFIKeyBlob { + pub key: veilid_core::DHTKey, + #[serde(with = "veilid_core::json_as_base64")] + pub blob: Vec, +} + // WASM Bindings #[wasm_bindgen()]