From 7f64a06609b785835cbdf3fbfb90ac57c60fbde5 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 2 Nov 2023 11:32:44 -0400 Subject: [PATCH 01/40] produce process_lib --- modules/terminal/Cargo.toml | 1 + process_lib/Cargo.toml | 11 + process_lib/src/lib.rs | 430 ++++++++++++++++++++++++++++++++++++ src/register | 2 +- 4 files changed, 443 insertions(+), 1 deletion(-) create mode 100644 process_lib/Cargo.toml create mode 100644 process_lib/src/lib.rs diff --git a/modules/terminal/Cargo.toml b/modules/terminal/Cargo.toml index 491485e6..fe91f049 100644 --- a/modules/terminal/Cargo.toml +++ b/modules/terminal/Cargo.toml @@ -17,6 +17,7 @@ cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-co serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = { version = "0.11.0", default_features = false } +uqbarprocesslib = { path = "../../uqbar-process-lib" } [lib] crate-type = ["cdylib"] diff --git a/process_lib/Cargo.toml b/process_lib/Cargo.toml new file mode 100644 index 00000000..bf455eb1 --- /dev/null +++ b/process_lib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "uqbarprocesslib" +version = "0.2.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.71" +bincode = "1.3.3" +serde = {version = "1.0", features = ["derive"] } +serde_json = "1.0" +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs new file mode 100644 index 00000000..0257b959 --- /dev/null +++ b/process_lib/src/lib.rs @@ -0,0 +1,430 @@ +/// Uqbar process standard library for Rust compiled to WASM +/// Must be used in context of bindings generated by uqbar.wit +use serde::{Deserialize, Serialize}; + +wit_bindgen::generate!({ + path: "../wit", + world: "uq-process", + exports: { + world: Component, + } +}); + +struct Component; + +impl Guest for Component { + fn init(_: Address) { + println!("Don't run this, run the app!"); + set_on_panic(&OnPanic::None); + } +} + +/// Override the println! macro to print to the terminal +macro_rules! println { + () => { + $print_to_terminal(0, "\n"); + }; + ($($arg:tt)*) => { + $print_to_terminal(0, &format!($($arg)*)); + }; + } + +/// PackageId is like a ProcessId, but for a package. Only contains the name +/// of the package and the name of the publisher. +#[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] +pub struct PackageId { + package_name: String, + publisher_node: String, +} + +impl PackageId { + pub fn new(package_name: &str, publisher_node: &str) -> Self { + PackageId { + package_name: package_name.into(), + publisher_node: publisher_node.into(), + } + } + pub fn from_str(input: &str) -> Result { + // split string on colons into 2 segments + let mut segments = input.split(':'); + let package_name = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + let publisher_node = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + if segments.next().is_some() { + return Err(ProcessIdParseError::TooManyColons); + } + Ok(PackageId { + package_name, + publisher_node, + }) + } + pub fn to_string(&self) -> String { + [self.package_name.as_str(), self.publisher_node.as_str()].join(":") + } + pub fn package(&self) -> &str { + &self.package_name + } + pub fn publisher_node(&self) -> &str { + &self.publisher_node + } +} + +/// ProcessId is defined in the wit bindings, but constructors and methods +/// are defined here. +impl ProcessId { + /// generates a random u64 number if process_name is not declared + pub fn new(process_name: &str, package_name: &str, publisher_node: &str) -> Self { + ProcessId { + process_name: process_name.into(), + package_name: package_name.into(), + publisher_node: publisher_node.into(), + } + } + pub fn from_str(input: &str) -> Result { + // split string on colons into 3 segments + let mut segments = input.split(':'); + let process_name = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + let package_name = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + let publisher_node = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + if segments.next().is_some() { + return Err(ProcessIdParseError::TooManyColons); + } + Ok(ProcessId { + process_name, + package_name, + publisher_node, + }) + } + pub fn to_string(&self) -> String { + [ + self.process_name.as_str(), + self.package_name.as_str(), + self.publisher_node.as_str(), + ] + .join(":") + } + pub fn process(&self) -> &str { + &self.process_name + } + pub fn package(&self) -> &str { + &self.package_name + } + pub fn publisher_node(&self) -> &str { + &self.publisher_node + } +} + +impl std::fmt::Display for ProcessId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}:{}:{}", + self.process_name, self.package_name, self.publisher_node + ) + } +} + +impl PartialEq for ProcessId { + fn eq(&self, other: &Self) -> bool { + self.process_name == other.process_name + && self.package_name == other.package_name + && self.publisher_node == other.publisher_node + } +} + +impl PartialEq<&str> for ProcessId { + fn eq(&self, other: &&str) -> bool { + &self.to_string() == other + } +} + +impl PartialEq for &str { + fn eq(&self, other: &ProcessId) -> bool { + self == &other.to_string() + } +} + +#[derive(Debug)] +pub enum ProcessIdParseError { + TooManyColons, + MissingField, +} + +impl std::fmt::Display for ProcessIdParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + ProcessIdParseError::TooManyColons => "Too many colons in ProcessId string", + ProcessIdParseError::MissingField => "Missing field in ProcessId string", + } + ) + } +} + +impl std::error::Error for ProcessIdParseError { + fn description(&self) -> &str { + match self { + ProcessIdParseError::TooManyColons => "Too many colons in ProcessId string", + ProcessIdParseError::MissingField => "Missing field in ProcessId string", + } + } +} + +/// Address is defined in the wit bindings, but constructors and methods here. +impl Address { + pub fn from_str(input: &str) -> Result { + // split string on colons into 4 segments, + // first one with @, next 3 with : + let mut name_rest = input.split('@'); + let node = name_rest + .next() + .ok_or(AddressParseError::MissingField)? + .to_string(); + let mut segments = name_rest + .next() + .ok_or(AddressParseError::MissingNodeId)? + .split(':'); + let process_name = segments + .next() + .ok_or(AddressParseError::MissingField)? + .to_string(); + let package_name = segments + .next() + .ok_or(AddressParseError::MissingField)? + .to_string(); + let publisher_node = segments + .next() + .ok_or(AddressParseError::MissingField)? + .to_string(); + if segments.next().is_some() { + return Err(AddressParseError::TooManyColons); + } + Ok(Address { + node, + process: ProcessId { + process_name, + package_name, + publisher_node, + }, + }) + } + pub fn to_string(&self) -> String { + [self.node.as_str(), &self.process.to_string()].join("@") + } +} + +#[derive(Debug)] +pub enum AddressParseError { + TooManyColons, + MissingNodeId, + MissingField, +} + +/// +/// Here, we define wrappers over the wit bindings to make them easier to use. +/// This library prescribes the use of IPC and metadata types serialized and +/// deserialized to JSON, which is far from optimal for performance, but useful +/// for applications that want to maximize composability and introspectability. +/// For payloads, we use bincode to serialize and deserialize to bytes. +/// + +pub fn send_typed_request( + target: &Address, + inherit_payload_and_context: bool, + ipc: &T1, + metadata: Option<&T2>, + context: Option<&T3>, + payload: Option<&T4>, + timeout: Option, +) -> anyhow::Result<()> +where + T1: serde::Serialize, + T2: serde::Serialize, + T3: serde::Serialize, + T4: serde::Serialize, +{ + let payload = match payload { + Some(payload) => Some(Payload { + mime: None, + bytes: bincode::serialize(payload)?, + }), + None => None, + }; + let context = match context { + Some(context) => Some(serde_json::to_vec(context)?), + None => None, + }; + crate::send_request( + target, + &Request { + inherit: inherit_payload_and_context, + expects_response: timeout, + ipc: serde_json::to_vec(ipc)?, + metadata: match metadata { + Some(metadata) => Some(serde_json::to_string(metadata)?), + None => None, + }, + }, + context.as_ref(), + payload.as_ref(), + ); + Ok(()) +} + +pub fn send_typed_response( + inherit_payload: bool, + ipc: &T1, + metadata: Option<&T2>, + payload: Option<&T3>, // will overwrite inherit flag if both are set +) -> anyhow::Result<()> +where + T1: serde::Serialize, + T2: serde::Serialize, + T3: serde::Serialize, +{ + let payload = match payload { + Some(payload) => Some(Payload { + mime: None, + bytes: bincode::serialize(payload)?, + }), + None => None, + }; + crate::send_response( + &Response { + inherit: inherit_payload, + ipc: serde_json::to_vec(ipc)?, + metadata: match metadata { + Some(metadata) => Some(serde_json::to_string(metadata)?), + None => None, + }, + }, + payload.as_ref(), + ); + Ok(()) +} + +pub fn send_and_await_typed_response( + target: &Address, + inherit_payload_and_context: bool, + ipc: &T1, + metadata: Option<&T2>, + payload: Option<&T3>, + timeout: u64, +) -> anyhow::Result> +where + T1: serde::Serialize, + T2: serde::Serialize, + T3: serde::Serialize, +{ + let payload = match payload { + Some(payload) => Some(Payload { + mime: None, + bytes: bincode::serialize(payload)?, + }), + None => None, + }; + let res = crate::send_and_await_response( + target, + &Request { + inherit: inherit_payload_and_context, + expects_response: Some(timeout), + ipc: serde_json::to_vec(ipc)?, + metadata: match metadata { + Some(metadata) => Some(serde_json::to_string(metadata)?), + None => None, + }, + }, + payload.as_ref(), + ); + Ok(res) +} + +pub fn get_typed_payload() -> Option { + match crate::get_payload() { + Some(payload) => match bincode::deserialize::(&payload.bytes) { + Ok(bytes) => Some(bytes), + Err(_) => None, + }, + None => None, + } +} + +pub fn get_typed_state() -> Option { + match crate::get_state() { + Some(bytes) => match bincode::deserialize::(&bytes) { + Ok(state) => Some(state), + Err(_) => None, + }, + None => None, + } +} + +pub fn set_typed_state(state: &T) -> anyhow::Result<()> +where + T: serde::Serialize, +{ + crate::set_state(&bincode::serialize(state)?); + Ok(()) +} + +pub fn grant_messaging(our: &Address, grant_to: &Vec) -> anyhow::Result<()> { + let Some(our_messaging_cap) = crate::get_capability( + our, + &"\"messaging\"".into() + ) else { + // the kernel will always give us this capability, so this should never happen + return Err(anyhow::anyhow!("failed to get our own messaging capability!")) + }; + for process in grant_to { + crate::share_capability(&process, &our_messaging_cap); + } + Ok(()) +} + +pub fn can_message(address: &Address) -> bool { + crate::get_capability(address, &"\"messaging\"".into()).is_some() +} + +/// +/// Here, we define types used by various Uqbar runtime components. Use these +/// to interface directly with the kernel, filesystem, virtual filesystem, +/// and other components -- if you have the capability to do so. +/// + +// move these to better place! +#[derive(Serialize, Deserialize, Debug)] +pub enum FsAction { + Write, + Replace(u128), + Append(Option), + Read(u128), + ReadChunk(ReadChunkRequest), + Delete(u128), + Length(u128), + // process state management + GetState, + SetState, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ReadChunkRequest { + pub file_uuid: u128, + pub start: u64, + pub length: u64, +} diff --git a/src/register b/src/register index 81654a0e..63cd0448 160000 --- a/src/register +++ b/src/register @@ -1 +1 @@ -Subproject commit 81654a0ed724a62748e49feb0c97fad50f1e243a +Subproject commit 63cd04480aefe8c9a3c185ee5dba4e9b6cdbddf8 From bbfeb418bd48a7034733777d26d3ea3564f0d400 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 2 Nov 2023 12:48:22 -0400 Subject: [PATCH 02/40] WIP --- modules/terminal/Cargo.toml | 1 - process_lib/src/lib.rs | 6 +++++- src/kernel/mod.rs | 25 ++++--------------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/modules/terminal/Cargo.toml b/modules/terminal/Cargo.toml index fe91f049..491485e6 100644 --- a/modules/terminal/Cargo.toml +++ b/modules/terminal/Cargo.toml @@ -17,7 +17,6 @@ cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-co serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = { version = "0.11.0", default_features = false } -uqbarprocesslib = { path = "../../uqbar-process-lib" } [lib] crate-type = ["cdylib"] diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index 0257b959..9698cff8 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -14,7 +14,11 @@ struct Component; impl Guest for Component { fn init(_: Address) { - println!("Don't run this, run the app!"); + print_to_terminal(0, "don't run this!"); + + std::thread::sleep(std::time::Duration::from_millis(3000)); + + set_on_panic(&OnPanic::None); } } diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 16a6336b..b7f54586 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -123,27 +123,10 @@ impl UqProcessImports for ProcessWasi { // process management: // - /// todo -> move to kernel logic to enable persistence etc. - async fn set_on_panic(&mut self, _on_panic: wit::OnPanic) -> Result<()> { - unimplemented!(); - // let on_panic = match on_panic { - // wit::OnPanic::None => t::OnPanic::None, - // wit::OnPanic::Restart => t::OnPanic::Restart, - // wit::OnPanic::Requests(reqs) => t::OnPanic::Requests( - // reqs.into_iter() - // .map(|(addr, req, payload)| { - // ( - // de_wit_address(addr), - // de_wit_request(req), - // de_wit_payload(payload), - // ) - // }) - // .collect(), - // ), - // }; - - // self.process.metadata.on_panic = on_panic; - // Ok(()) + /// TODO critical: move to kernel logic to enable persistence of choice made here + async fn set_on_panic(&mut self, on_panic: wit::OnPanic) -> Result<()> { + self.process.metadata.on_panic = de_wit_on_panic(on_panic); + Ok(()) } /// create a message from the *kernel* to the filesystem, From ca91d5d920947e502798ec5576de5f159782dd18 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 2 Nov 2023 15:35:35 -0400 Subject: [PATCH 03/40] working!!!! (terminal only) --- build.rs | 4 + modules/terminal/Cargo.lock | 197 +++++----------------------- modules/terminal/Cargo.toml | 4 +- modules/terminal/src/lib.rs | 14 +- modules/terminal/src/process_lib.rs | 1 - process_lib/Cargo.toml | 2 +- process_lib/src/lib.rs | 19 +-- src/kernel/mod.rs | 12 +- wit/uqbar.wit | 61 +++++---- 9 files changed, 92 insertions(+), 222 deletions(-) delete mode 120000 modules/terminal/src/process_lib.rs diff --git a/build.rs b/build.rs index 0dea067a..a70a7a57 100644 --- a/build.rs +++ b/build.rs @@ -160,6 +160,10 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); + if package_name != "terminal" { + continue; + } + // If Cargo.toml is present, build the app let parent_pkg_path = format!("{}/pkg", entry_path.display()); if entry_path.join("Cargo.toml").exists() { diff --git a/modules/terminal/Cargo.lock b/modules/terminal/Cargo.lock index 494c2236..e96caa32 100644 --- a/modules/terminal/Cargo.lock +++ b/modules/terminal/Cargo.lock @@ -17,57 +17,18 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -[[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] - [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - [[package]] name = "hashbrown" version = "0.14.0" @@ -89,16 +50,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" version = "2.0.0" @@ -128,18 +79,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - [[package]] name = "proc-macro2" version = "1.0.66" @@ -149,17 +88,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "quote" version = "1.0.33" @@ -244,57 +172,18 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "serde", "serde_json", + "uqbar_process_lib", "wit-bindgen", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -308,40 +197,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "serde", + "serde_json", + "wit-bindgen", ] -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -350,9 +234,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" dependencies = [ "indexmap", "semver", @@ -360,19 +244,17 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ - "bitflags 2.4.0", + "bitflags", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -381,54 +263,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -438,16 +308,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/terminal/Cargo.toml b/modules/terminal/Cargo.toml index 491485e6..46dcc666 100644 --- a/modules/terminal/Cargo.toml +++ b/modules/terminal/Cargo.toml @@ -13,10 +13,10 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +uqbar_process_lib = { path = "../../process_lib" } [lib] crate-type = ["cdylib"] diff --git a/modules/terminal/src/lib.rs b/modules/terminal/src/lib.rs index 8e569683..9ed41a8d 100644 --- a/modules/terminal/src/lib.rs +++ b/modules/terminal/src/lib.rs @@ -1,8 +1,11 @@ -cargo_component_bindings::generate!(); -use bindings::{component::uq_process::types::*, print_to_terminal, receive, send_request, Guest}; +use uqbar_process_lib::component::uq_process::api::*; -#[allow(dead_code)] -mod process_lib; +wit_bindgen::generate!({ + world: "uq-process", + exports: { + world: Component, + }, +}); struct Component; @@ -83,7 +86,8 @@ fn parse_command(our_name: &str, line: &str) { } impl Guest for Component { - fn init(our: Address) { + fn init(our: String) { + let our = Address::from_str(&our).unwrap(); assert_eq!(our.process.to_string(), "terminal:terminal:uqbar"); print_to_terminal(1, &format!("terminal: start")); loop { diff --git a/modules/terminal/src/process_lib.rs b/modules/terminal/src/process_lib.rs deleted file mode 120000 index 77367fe0..00000000 --- a/modules/terminal/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../src/process_lib.rs \ No newline at end of file diff --git a/process_lib/Cargo.toml b/process_lib/Cargo.toml index bf455eb1..927ff791 100644 --- a/process_lib/Cargo.toml +++ b/process_lib/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "uqbarprocesslib" +name = "uqbar_process_lib" version = "0.2.0" edition = "2021" diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index 9698cff8..3b42c4ab 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -1,28 +1,13 @@ /// Uqbar process standard library for Rust compiled to WASM /// Must be used in context of bindings generated by uqbar.wit use serde::{Deserialize, Serialize}; +use crate::component::uq_process::api::*; wit_bindgen::generate!({ path: "../wit", - world: "uq-process", - exports: { - world: Component, - } + world: "uq-process-lib", }); -struct Component; - -impl Guest for Component { - fn init(_: Address) { - print_to_terminal(0, "don't run this!"); - - std::thread::sleep(std::time::Duration::from_millis(3000)); - - - set_on_panic(&OnPanic::None); - } -} - /// Override the println! macro to print to the terminal macro_rules! println { () => { diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index b7f54586..24e0d95d 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -20,7 +20,11 @@ use crate::KERNEL_PROCESS_ID; use crate::VFS_PROCESS_ID; // WIT errors when `use`ing interface unless we import this and implement Host for Process below use crate::kernel::component::uq_process::types as wit; -use crate::kernel::component::uq_process::types::Host; +use crate::kernel::wit::Host; +// use crate::kernel::component::uq_process::types::Host; +// use crate::kernel::component::uq_process::api::Host; + +use component::uq_process::api::Host as Foo; mod utils; use crate::kernel::utils::*; @@ -98,7 +102,7 @@ impl WasiView for ProcessWasi { /// create the process API. this is where the functions that a process can use live. /// #[async_trait::async_trait] -impl UqProcessImports for ProcessWasi { +impl Foo for ProcessWasi { // // system utils: // @@ -193,7 +197,7 @@ impl UqProcessImports for ProcessWasi { .unwrap(), metadata: None, }, - Some(Payload { mime: None, bytes }), + Some(wit::Payload { mime: None, bytes }), ) .await { @@ -1181,7 +1185,7 @@ async fn make_process_loop( }; // the process will run until it returns from init() - let is_error = match bindings.call_init(&mut store, &metadata.our.en_wit()).await { + let is_error = match bindings.call_init(&mut store, &metadata.our.to_string()).await { Ok(()) => { let _ = send_to_terminal diff --git a/wit/uqbar.wit b/wit/uqbar.wit index 232bc721..e785ebb1 100644 --- a/wit/uqbar.wit +++ b/wit/uqbar.wit @@ -102,49 +102,42 @@ interface types { } } -world uq-process { +interface api { use types.{ json, node-id, context, process-id, address, - payload, request, response, message, - capabilities, signed-capability, - on-panic, send-error, send-error-kind, - spawn-error + spawn-error, } - // entry point to all programs - export init: func(our: address) - // system utils: - - import print-to-terminal: func(verbosity: u8, message: string) + print-to-terminal: func(verbosity: u8, message: string) // **more will be added here with regard to blockchains** - import get-eth-block: func() -> u64 + get-eth-block: func() -> u64 // process management: - import set-on-panic: func(on-panic: on-panic) + set-on-panic: func(on-panic: on-panic) - import get-state: func() -> option> + get-state: func() -> option> - import set-state: func(bytes: list) + set-state: func(bytes: list) - import clear-state: func() + clear-state: func() - import spawn: func( + spawn: func( name: option, wasm-path: string, // must be located within package's drive on-panic: on-panic, @@ -155,49 +148,59 @@ world uq-process { // capabilities management // gives us all our signed capabilities so we can send them to others - import get-capabilities: func() -> list + get-capabilities: func() -> list // gets a single specific capability - import get-capability: func(issuer: address, params: json) -> option + get-capability: func(issuer: address, params: json) -> option // attaches a specific signed capability to our next message - import attach-capability: func(capability: signed-capability) + attach-capability: func(capability: signed-capability) // saves capabilities to our store, so we can use them - import save-capabilities: func(capabilities: list) + save-capabilities: func(capabilities: list) // check to see if the sender of a prompting message has a given capability, issued by us // if the prompting message has a remote source, they must have attached it. - import has-capability: func(params: json) -> bool + has-capability: func(params: json) -> bool // generates a new capability with our process as the issuer and gives it to the target, // which must be a locally-running process. - import create-capability: func(to: process-id, params: json) + create-capability: func(to: process-id, params: json) // take a signed capability and save it to a given locally-running process - import share-capability: func(to: process-id, capability: signed-capability) + share-capability: func(to: process-id, capability: signed-capability) // message I/O: // ingest next message when it arrives along with its source. // almost all long-running processes will call this in a loop - import receive: func() -> result, tuple>> + receive: func() -> result, tuple>> // gets payload, if any, of the message we just received - import get-payload: func() -> option + get-payload: func() -> option // send message(s) to target(s) - import send-request: + send-request: func(target: address, request: request, context: option, payload: option) - import send-requests: + send-requests: func(requests: list, option>>) - import send-response: + send-response: func(response: response, payload: option) // send a single request, then block (internally) until its response // the type is Message but will always contain Response - import send-and-await-response: + send-and-await-response: func(target: address, request: request, payload: option) -> result, send-error> } + +world uq-process-lib { + import api +} + +world uq-process { + include uq-process-lib + + export init: func(our: string) +} From 7a06eaf016fae5a8b4c4d531f47041f8230f5834 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 19:36:04 +0000 Subject: [PATCH 04/40] Format Rust code using rustfmt --- src/kernel/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 24e0d95d..a4440668 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1185,7 +1185,10 @@ async fn make_process_loop( }; // the process will run until it returns from init() - let is_error = match bindings.call_init(&mut store, &metadata.our.to_string()).await { + let is_error = match bindings + .call_init(&mut store, &metadata.our.to_string()) + .await + { Ok(()) => { let _ = send_to_terminal From a1dbed6f26a18ae6fae822976762854a67307254 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 2 Nov 2023 15:42:11 -0400 Subject: [PATCH 05/40] comment unused macro --- process_lib/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index 3b42c4ab..43e6d9f8 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -9,14 +9,14 @@ wit_bindgen::generate!({ }); /// Override the println! macro to print to the terminal -macro_rules! println { - () => { - $print_to_terminal(0, "\n"); - }; - ($($arg:tt)*) => { - $print_to_terminal(0, &format!($($arg)*)); - }; - } +// macro_rules! println { +// () => { +// $print_to_terminal(0, "\n"); +// }; +// ($($arg:tt)*) => { +// $print_to_terminal(0, &format!($($arg)*)); +// }; +// } /// PackageId is like a ProcessId, but for a package. Only contains the name /// of the package and the name of the publisher. From 884fefc4f9f6e5c5eab3e647c33a85f3f3f8bc93 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 2 Nov 2023 16:42:18 -0400 Subject: [PATCH 06/40] app store and terminal working! --- build.rs | 13 +- .../app_store/app_store/Cargo-component.lock | 3 - modules/app_store/app_store/Cargo.lock | 107 ++--- modules/app_store/app_store/Cargo.toml | 4 +- .../app_store/app_store/src/kernel_types.rs | 1 - modules/app_store/app_store/src/lib.rs | 125 +++--- .../app_store/app_store/src/process_lib.rs | 1 - modules/app_store/ft_worker/Cargo.lock | 103 ++--- modules/app_store/ft_worker/Cargo.toml | 4 +- .../app_store/ft_worker/src/ft_worker_lib.rs | 23 +- modules/app_store/ft_worker/src/lib.rs | 24 +- .../app_store/ft_worker/src/process_lib.rs | 1 - modules/terminal/Cargo-component.lock | 3 - modules/terminal/Cargo.lock | 44 +- modules/terminal/src/lib.rs | 1 + process_lib/src/kernel_types.rs | 422 ++++++++++++++++++ process_lib/src/lib.rs | 89 ++-- src/main.rs | 3 +- 18 files changed, 647 insertions(+), 324 deletions(-) delete mode 100644 modules/app_store/app_store/Cargo-component.lock delete mode 120000 modules/app_store/app_store/src/kernel_types.rs delete mode 120000 modules/app_store/app_store/src/process_lib.rs delete mode 120000 modules/app_store/ft_worker/src/process_lib.rs delete mode 100644 modules/terminal/Cargo-component.lock create mode 100644 process_lib/src/kernel_types.rs diff --git a/build.rs b/build.rs index a70a7a57..afe98911 100644 --- a/build.rs +++ b/build.rs @@ -40,18 +40,11 @@ fn build_app(target_path: &str, name: &str, parent_pkg_path: Option<&str>) { // if and only if module's wit is outdated, re-set-up build environment if file_outdated( - format!("{}/wit/uqbar.wit", pwd.display()), - format!("{}/wit/uqbar.wit", target_path), + format!("{}/target.wasm", pwd.display()), + format!("{}/target/bindings/{}/target.wasm", target_path, name), ) .unwrap_or(true) { - println!("cargo:warning=wit outdated, rebuilding"); - run_command(Command::new("cp").args(&[ - "-r", - &format!("{}/wit", pwd.display()), - &format!("{}/wit", target_path), - ])) - .unwrap(); // create target/bindings directory fs::create_dir_all(&format!("{}/target/bindings/{}", target_path, name,)).unwrap(); // copy newly-made target.wasm into target/bindings @@ -160,7 +153,7 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if package_name != "terminal" { + if package_name != "terminal" && package_name != "app_store" { continue; } diff --git a/modules/app_store/app_store/Cargo-component.lock b/modules/app_store/app_store/Cargo-component.lock deleted file mode 100644 index 00bc239d..00000000 --- a/modules/app_store/app_store/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/modules/app_store/app_store/Cargo.lock b/modules/app_store/app_store/Cargo.lock index 70700207..a34d6800 100644 --- a/modules/app_store/app_store/Cargo.lock +++ b/modules/app_store/app_store/Cargo.lock @@ -14,12 +14,12 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "rand", "serde", "serde_json", "sha2", - "wit-bindgen 0.11.0", + "uqbar_process_lib", + "wit-bindgen", ] [[package]] @@ -46,29 +46,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "cargo-component-bindings" -version = "0.3.0" -source = "git+https://github.com/bytecodealliance/cargo-component#5cddf6df7fd5ab5aa9bfd66f4cf2e2f6cc7d72f9" -dependencies = [ - "cargo-component-macro", - "wit-bindgen 0.13.0", -] - -[[package]] -name = "cargo-component-macro" -version = "0.3.0" -source = "git+https://github.com/bytecodealliance/cargo-component#5cddf6df7fd5ab5aa9bfd66f4cf2e2f6cc7d72f9" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -77,9 +54,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -154,9 +131,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", @@ -255,18 +232,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -275,9 +252,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -345,6 +322,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "uqbar_process_lib" +version = "0.2.0" +dependencies = [ + "anyhow", + "bincode", + "serde", + "serde_json", + "wit-bindgen", +] + [[package]] name = "version_check" version = "0.9.4" @@ -359,18 +347,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.35.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca90ba1b5b0a70d3d49473c5579951f3bddc78d47b59256d2f9d4922b150aca" +checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.9" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14abc161bfda5b519aa229758b68f2a52b45a12b993808665c857d1a9a00223c" +checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" dependencies = [ "anyhow", "indexmap", @@ -384,9 +372,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.115.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e06c0641a4add879ba71ccb3a1e4278fd546f76f1eafb21d8f7b07733b547cd5" +checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" dependencies = [ "indexmap", "semver", @@ -394,18 +382,8 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" -dependencies = [ - "bitflags", -] - -[[package]] -name = "wit-bindgen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d92ce0ca6b6074059413a9581a637550c3a740581c854f9847ec293c8aed71" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -413,9 +391,8 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565b945ae074886071eccf9cdaf8ccd7b959c2b0d624095bea5fe62003e8b3e0" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -424,9 +401,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5695ff4e41873ed9ce56d2787e6b5772bdad9e70e2c1d2d160621d1762257f4f" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -437,9 +413,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91835ea4231da1fe7971679d505ba14be7826e192b6357f08465866ef482e08" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", @@ -452,9 +427,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87488b57a08e2cbbd076b325acbe7f8666965af174d69d5929cd373bd54547f" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", "bitflags", @@ -471,9 +446,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ace9943d89bbf3dbbc71b966da0e7302057b311f36a4ac3d65ddfef17b52cf" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", diff --git a/modules/app_store/app_store/Cargo.toml b/modules/app_store/app_store/Cargo.toml index 4e095cb2..18709cd2 100644 --- a/modules/app_store/app_store/Cargo.toml +++ b/modules/app_store/app_store/Cargo.toml @@ -13,12 +13,12 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } rand = "0.8.5" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" sha2 = "0.10.8" -wit-bindgen = { version = "0.11.0", default_features = false } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +uqbar_process_lib = { path = "../../../process_lib" } [lib] crate-type = ["cdylib"] diff --git a/modules/app_store/app_store/src/kernel_types.rs b/modules/app_store/app_store/src/kernel_types.rs deleted file mode 120000 index 047e48bc..00000000 --- a/modules/app_store/app_store/src/kernel_types.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/kernel_types.rs \ No newline at end of file diff --git a/modules/app_store/app_store/src/lib.rs b/modules/app_store/app_store/src/lib.rs index b7803ca1..c6202403 100644 --- a/modules/app_store/app_store/src/lib.rs +++ b/modules/app_store/app_store/src/lib.rs @@ -1,20 +1,18 @@ -cargo_component_bindings::generate!(); -use bindings::{ - component::uq_process::types::*, get_capability, get_payload, print_to_terminal, receive, - send_request, send_response, Guest, -}; use serde::{Deserialize, Serialize}; use sha2::Digest; use std::collections::{HashMap, HashSet}; +use uqbar_process_lib::component::uq_process::api::*; +use uqbar_process_lib::component::uq_process::types::NodeId; +use uqbar_process_lib::*; +use uqbar_process_lib::kernel_types as kt; -#[allow(dead_code)] -mod kernel_types; -use kernel_types as kt; -use kernel_types::{PackageManifestEntry, PackageMetadata, PackageVersion}; - -#[allow(dead_code)] -mod process_lib; -use process_lib::PackageId; +wit_bindgen::generate!({ + path: "../../../wit", + world: "uq-process", + exports: { + world: Component, + }, +}); #[allow(dead_code)] mod ft_worker_lib; @@ -66,7 +64,7 @@ struct PackageListing { pub publisher: NodeId, pub description: Option, pub website: Option, - pub version: PackageVersion, + pub version: kt::PackageVersion, pub version_hash: String, // sha256 hash of the package zip or whatever } @@ -163,12 +161,13 @@ pub enum InstallResponse { // impl Guest for Component { - fn init(our: Address) { + fn init(our: String) { + let our = Address::from_str(&our).unwrap(); assert_eq!(our.process, "main:app_store:uqbar"); // begin by granting messaging capabilities to http_server and terminal, // so that they can send us requests. - process_lib::grant_messaging( + grant_messaging( &our, &Vec::from([ ProcessId::from_str("http_server:sys:uqbar").unwrap(), @@ -178,7 +177,7 @@ impl Guest for Component { print_to_terminal(0, &format!("app_store main proc: start")); // load in our saved state or initalize a new one if none exists - let mut state = process_lib::get_state::().unwrap_or(State { + let mut state = get_typed_state::().unwrap_or(State { packages: HashMap::new(), requested_packages: HashSet::new(), }); @@ -377,68 +376,68 @@ fn handle_local_request( hasher.update(&payload.bytes); let version_hash = format!("{:x}", hasher.finalize()); - let _ = process_lib::send_and_await_response( + send_and_await_typed_response( &vfs_address, false, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::New, - })?, + }, None, None, 5, - )?; + )??; // add zip bytes payload.mime = Some("application/zip".to_string()); - let _ = process_lib::send_and_await_response( + send_and_await_typed_response( &vfs_address, true, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::Add { full_path: package.to_string(), entry_type: kt::AddEntryType::ZipArchive, }, - })?, + }, None, Some(&payload), 5, - )?; + )??; // save the zip file itself in VFS for sharing with other nodes // call it .zip - let _ = process_lib::send_and_await_response( + send_and_await_typed_response( &vfs_address, true, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::Add { full_path: format!("/{}.zip", package.to_string()), entry_type: kt::AddEntryType::NewFile, }, - })?, + }, None, Some(&payload), 5, - )?; + )??; - let _ = process_lib::send_and_await_response( + send_and_await_typed_response( &vfs_address, false, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry("/metadata.json".into()), - })?, + }, None, None, 5, - )?; + )??; let Some(payload) = get_payload() else { return Err(anyhow::anyhow!("no metadata payload")); }; let metadata = String::from_utf8(payload.bytes)?; - let metadata = serde_json::from_str::(&metadata)?; + let metadata = serde_json::from_str::(&metadata)?; let listing_data = PackageListing { name: metadata.package, @@ -455,30 +454,30 @@ fn handle_local_request( auto_update: true, }; state.packages.insert(package.clone(), package_state); - process_lib::set_state::(&state); + set_typed_state::(&state); Ok(Some(Resp::NewPackageResponse(NewPackageResponse::Success))) } LocalRequest::Download { package, install_from, } => Ok(Some(Resp::DownloadResponse( - match process_lib::send_and_await_response( + match send_and_await_typed_response( &Address { node: install_from.clone(), process: our.process.clone(), }, true, - serde_json::to_vec(&RemoteRequest::Download(package.clone()))?, + &RemoteRequest::Download(package.clone()), None, None, 5, ) { - Ok((_source, Message::Response((resp, _context)))) => { + Ok(Ok((_source, Message::Response((resp, _context))))) => { let resp = serde_json::from_slice::(&resp.ipc)?; match resp { Resp::RemoteResponse(RemoteResponse::DownloadApproved) => { state.requested_packages.insert(package.clone()); - process_lib::set_state::(&state); + set_typed_state::(&state); DownloadResponse::Started } _ => DownloadResponse::Failure, @@ -492,22 +491,22 @@ fn handle_local_request( node: our.node.clone(), process: ProcessId::from_str("vfs:sys:uqbar")?, }; - let _ = process_lib::send_and_await_response( + send_and_await_typed_response( &vfs_address, false, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry("/manifest.json".into()), - })?, + }, None, None, 5, - )?; + )??; let Some(payload) = get_payload() else { return Err(anyhow::anyhow!("no payload")); }; let manifest = String::from_utf8(payload.bytes)?; - let manifest = serde_json::from_str::>(&manifest)?; + let manifest = serde_json::from_str::>(&manifest)?; for entry in manifest { let path = if entry.process_wasm_path.starts_with("/") { entry.process_wasm_path @@ -515,17 +514,17 @@ fn handle_local_request( format!("/{}", entry.process_wasm_path) }; - let (_, hash_response) = process_lib::send_and_await_response( + let (_, hash_response) = send_and_await_typed_response( &vfs_address, false, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetHash(path.clone()), - })?, + }, None, None, 5, - )?; + )??; let Message::Response((Response { ipc, .. }, _)) = hash_response else { return Err(anyhow::anyhow!("bad vfs response")); @@ -591,15 +590,15 @@ fn handle_local_request( let Ok(parsed_new_process_id) = ProcessId::from_str(&process_id) else { return Err(anyhow::anyhow!("app-store: invalid process id!")); }; - let _ = process_lib::send_request( + send_typed_request( &Address { node: our.node.clone(), process: ProcessId::from_str("kernel:sys:uqbar")?, }, false, - serde_json::to_vec(&kt::KernelCommand::KillProcess(kt::ProcessId::de_wit( + &kt::KernelCommand::KillProcess(kt::ProcessId::de_wit( parsed_new_process_id.clone(), - )))?, + )), None, None, None, @@ -607,39 +606,39 @@ fn handle_local_request( // kernel start process takes bytes as payload + wasm_bytes_handle... // reconsider perhaps - let (_, _bytes_response) = process_lib::send_and_await_response( + let (_, _bytes_response) = send_and_await_typed_response( &vfs_address, false, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry(path), - })?, + }, None, None, 5, - )?; + )??; let Some(payload) = get_payload() else { return Err(anyhow::anyhow!("no wasm bytes payload.")); }; - let _ = process_lib::send_and_await_response( + send_and_await_typed_response( &Address { node: our.node.clone(), process: ProcessId::from_str("kernel:sys:uqbar")?, }, false, - serde_json::to_vec(&kt::KernelCommand::StartProcess { + &kt::KernelCommand::StartProcess { id: kt::ProcessId::de_wit(parsed_new_process_id), wasm_bytes_handle: hash, on_panic: entry.on_panic, initial_capabilities, public: entry.public, - })?, + }, None, Some(&payload), 5, - )?; + )??; } Ok(Some(Resp::InstallResponse(InstallResponse::Success))) } @@ -674,17 +673,17 @@ fn handle_remote_request( process: ProcessId::from_str("vfs:sys:uqbar")?, }; let file_name = format!("/{}.zip", package.to_string()); - let _ = process_lib::send_and_await_response( + send_and_await_typed_response( &vfs_address, false, - serde_json::to_vec(&kt::VfsRequest { + &kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry(file_name.clone()), - })?, + }, None, None, 5, - )?; + )??; // transfer will inherit the payload bytes we receive from VFS spawn_transfer(&our, &file_name, None, &source); Ok(Some(Resp::RemoteResponse(RemoteResponse::DownloadApproved))) diff --git a/modules/app_store/app_store/src/process_lib.rs b/modules/app_store/app_store/src/process_lib.rs deleted file mode 120000 index 9b9ec3f4..00000000 --- a/modules/app_store/app_store/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/process_lib.rs \ No newline at end of file diff --git a/modules/app_store/ft_worker/Cargo.lock b/modules/app_store/ft_worker/Cargo.lock index e882100a..896c5fa8 100644 --- a/modules/app_store/ft_worker/Cargo.lock +++ b/modules/app_store/ft_worker/Cargo.lock @@ -23,29 +23,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" -[[package]] -name = "cargo-component-bindings" -version = "0.3.0" -source = "git+https://github.com/bytecodealliance/cargo-component#5cddf6df7fd5ab5aa9bfd66f4cf2e2f6cc7d72f9" -dependencies = [ - "cargo-component-macro", - "wit-bindgen 0.13.0", -] - -[[package]] -name = "cargo-component-macro" -version = "0.3.0" -source = "git+https://github.com/bytecodealliance/cargo-component#5cddf6df7fd5ab5aa9bfd66f4cf2e2f6cc7d72f9" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -64,11 +41,11 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "rand", "serde", "serde_json", - "wit-bindgen 0.11.0", + "uqbar_process_lib", + "wit-bindgen", ] [[package]] @@ -105,9 +82,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", @@ -206,18 +183,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -226,9 +203,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -279,6 +256,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "uqbar_process_lib" +version = "0.2.0" +dependencies = [ + "anyhow", + "bincode", + "serde", + "serde_json", + "wit-bindgen", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -287,18 +275,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.35.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca90ba1b5b0a70d3d49473c5579951f3bddc78d47b59256d2f9d4922b150aca" +checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.9" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14abc161bfda5b519aa229758b68f2a52b45a12b993808665c857d1a9a00223c" +checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" dependencies = [ "anyhow", "indexmap", @@ -312,9 +300,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.115.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e06c0641a4add879ba71ccb3a1e4278fd546f76f1eafb21d8f7b07733b547cd5" +checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" dependencies = [ "indexmap", "semver", @@ -322,18 +310,8 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" -dependencies = [ - "bitflags", -] - -[[package]] -name = "wit-bindgen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d92ce0ca6b6074059413a9581a637550c3a740581c854f9847ec293c8aed71" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -341,9 +319,8 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565b945ae074886071eccf9cdaf8ccd7b959c2b0d624095bea5fe62003e8b3e0" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -352,9 +329,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5695ff4e41873ed9ce56d2787e6b5772bdad9e70e2c1d2d160621d1762257f4f" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -365,9 +341,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91835ea4231da1fe7971679d505ba14be7826e192b6357f08465866ef482e08" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", @@ -380,9 +355,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87488b57a08e2cbbd076b325acbe7f8666965af174d69d5929cd373bd54547f" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", "bitflags", @@ -399,9 +374,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ace9943d89bbf3dbbc71b966da0e7302057b311f36a4ac3d65ddfef17b52cf" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", diff --git a/modules/app_store/ft_worker/Cargo.toml b/modules/app_store/ft_worker/Cargo.toml index d6331893..5d9a3b60 100644 --- a/modules/app_store/ft_worker/Cargo.toml +++ b/modules/app_store/ft_worker/Cargo.toml @@ -13,11 +13,11 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } rand = "0.8" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +uqbar_process_lib = { path = "../../../process_lib" } [lib] crate-type = ["cdylib"] diff --git a/modules/app_store/ft_worker/src/ft_worker_lib.rs b/modules/app_store/ft_worker/src/ft_worker_lib.rs index e48faf71..a960fa66 100644 --- a/modules/app_store/ft_worker/src/ft_worker_lib.rs +++ b/modules/app_store/ft_worker/src/ft_worker_lib.rs @@ -1,6 +1,5 @@ -use super::bindings::component::uq_process::types::*; -use super::bindings::{print_to_terminal, send_request, spawn, Address, Payload}; use serde::{Deserialize, Serialize}; +use uqbar_process_lib::component::uq_process::api::*; #[derive(Debug, Serialize, Deserialize)] pub struct FileTransferContext { @@ -84,15 +83,17 @@ pub fn spawn_transfer( .unwrap(), metadata: None, }, - Some(&serde_json::to_vec(&FileTransferContext { - file_name: file_name.into(), - file_size: match &payload_or_inherit { - Some(p) => Some(p.bytes.len() as u64), - None => None, // TODO - }, - start_time: std::time::SystemTime::now(), - }) - .unwrap()), + Some( + &serde_json::to_vec(&FileTransferContext { + file_name: file_name.into(), + file_size: match &payload_or_inherit { + Some(p) => Some(p.bytes.len() as u64), + None => None, // TODO + }, + start_time: std::time::SystemTime::now(), + }) + .unwrap(), + ), payload_or_inherit.as_ref(), ); } diff --git a/modules/app_store/ft_worker/src/lib.rs b/modules/app_store/ft_worker/src/lib.rs index 393757bd..e4aaaadd 100644 --- a/modules/app_store/ft_worker/src/lib.rs +++ b/modules/app_store/ft_worker/src/lib.rs @@ -1,15 +1,20 @@ -cargo_component_bindings::generate!(); -use bindings::component::uq_process::types::*; -use bindings::{get_payload, print_to_terminal, receive, send_request, send_response, Guest}; use serde::{Deserialize, Serialize}; - -struct Component; +use uqbar_process_lib::component::uq_process::api::*; +use uqbar_process_lib::component::uq_process::types::SendErrorKind; mod ft_worker_lib; -#[allow(dead_code)] -mod process_lib; use ft_worker_lib::*; +wit_bindgen::generate!({ + path: "../../../wit", + world: "uq-process", + exports: { + world: Component, + }, +}); + +struct Component; + /// internal worker protocol #[derive(Debug, Serialize, Deserialize)] pub enum FTWorkerProtocol { @@ -18,7 +23,8 @@ pub enum FTWorkerProtocol { } impl Guest for Component { - fn init(our: Address) { + fn init(our: String) { + let our = Address::from_str(&our).unwrap(); print_to_terminal(1, &format!("{}: start", our.process)); let Ok((parent_process, Message::Request(req))) = receive() else { @@ -49,7 +55,7 @@ impl Guest for Component { // then upon reciving affirmative response, // send contents in chunks and wait for // acknowledgement. - match bindings::send_and_await_response( + match send_and_await_response( &Address::from_str(&target).unwrap(), &Request { inherit: false, diff --git a/modules/app_store/ft_worker/src/process_lib.rs b/modules/app_store/ft_worker/src/process_lib.rs deleted file mode 120000 index 9b9ec3f4..00000000 --- a/modules/app_store/ft_worker/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/process_lib.rs \ No newline at end of file diff --git a/modules/terminal/Cargo-component.lock b/modules/terminal/Cargo-component.lock deleted file mode 100644 index 00bc239d..00000000 --- a/modules/terminal/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/modules/terminal/Cargo.lock b/modules/terminal/Cargo.lock index e96caa32..c05a6991 100644 --- a/modules/terminal/Cargo.lock +++ b/modules/terminal/Cargo.lock @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "equivalent" @@ -31,9 +31,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -52,9 +52,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", @@ -81,9 +81,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -105,24 +105,24 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -131,9 +131,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "spdx" @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.31" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" diff --git a/modules/terminal/src/lib.rs b/modules/terminal/src/lib.rs index 9ed41a8d..9c00d2b5 100644 --- a/modules/terminal/src/lib.rs +++ b/modules/terminal/src/lib.rs @@ -1,6 +1,7 @@ use uqbar_process_lib::component::uq_process::api::*; wit_bindgen::generate!({ + path: "../../wit", world: "uq-process", exports: { world: Component, diff --git a/process_lib/src/kernel_types.rs b/process_lib/src/kernel_types.rs new file mode 100644 index 00000000..9df17865 --- /dev/null +++ b/process_lib/src/kernel_types.rs @@ -0,0 +1,422 @@ +use crate::component::uq_process::api as wit; +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; + +// +// process-facing kernel types, used for process +// management and message-passing +// matches types in uqbar.wit +// + +pub type Context = Vec; +pub type NodeId = String; // QNS domain name + +/// process ID is a formatted unique identifier that contains +/// the publishing node's ID, the package name, and finally the process name. +/// the process name can be a random number, or a name chosen by the user. +/// the formatting is as follows: +/// `[process name]:[package name]:[node ID]` +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct ProcessId { + process_name: String, + package_name: String, + publisher_node: NodeId, +} + +#[allow(dead_code)] +impl ProcessId { + /// generates a random u64 number if process_name is not declared + pub fn new(process_name: &str, package_name: &str, publisher_node: &str) -> Self { + ProcessId { + process_name: process_name.into(), + package_name: package_name.into(), + publisher_node: publisher_node.into(), + } + } + pub fn from_str(input: &str) -> Result { + // split string on colons into 3 segments + let mut segments = input.split(':'); + let process_name = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + let package_name = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + let publisher_node = segments + .next() + .ok_or(ProcessIdParseError::MissingField)? + .to_string(); + if segments.next().is_some() { + return Err(ProcessIdParseError::TooManyColons); + } + Ok(ProcessId { + process_name, + package_name, + publisher_node, + }) + } + pub fn to_string(&self) -> String { + [ + self.process_name.as_str(), + self.package_name.as_str(), + self.publisher_node.as_str(), + ] + .join(":") + } + pub fn process(&self) -> &str { + &self.process_name + } + pub fn package(&self) -> &str { + &self.package_name + } + pub fn publisher_node(&self) -> &str { + &self.publisher_node + } + pub fn en_wit(&self) -> wit::ProcessId { + wit::ProcessId { + process_name: self.process_name.clone(), + package_name: self.package_name.clone(), + publisher_node: self.publisher_node.clone(), + } + } + pub fn de_wit(wit: wit::ProcessId) -> ProcessId { + ProcessId { + process_name: wit.process_name, + package_name: wit.package_name, + publisher_node: wit.publisher_node, + } + } +} + +#[derive(Debug)] +pub enum ProcessIdParseError { + TooManyColons, + MissingField, +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub struct Address { + pub node: NodeId, + pub process: ProcessId, +} + +impl Address { + pub fn en_wit(&self) -> wit::Address { + wit::Address { + node: self.node.clone(), + process: self.process.en_wit(), + } + } + pub fn de_wit(wit: wit::Address) -> Address { + Address { + node: wit.node, + process: ProcessId { + process_name: wit.process.process_name, + package_name: wit.process.package_name, + publisher_node: wit.process.publisher_node, + }, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Payload { + pub mime: Option, // MIME type + pub bytes: Vec, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Request { + pub inherit: bool, + pub expects_response: Option, // number of seconds until timeout + pub ipc: Vec, + pub metadata: Option, // JSON-string +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Response { + pub inherit: bool, + pub ipc: Vec, + pub metadata: Option, // JSON-string +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Message { + Request(Request), + Response((Response, Option)), +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct Capability { + pub issuer: Address, + pub params: String, // JSON-string +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct SignedCapability { + pub issuer: Address, + pub params: String, // JSON-string + pub signature: Vec, // signed by the kernel, so we can verify that the kernel issued it +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SendError { + pub kind: SendErrorKind, + pub target: Address, + pub message: Message, + pub payload: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum SendErrorKind { + Offline, + Timeout, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum OnPanic { + None, + Restart, + Requests(Vec<(Address, Request, Option)>), +} + +impl OnPanic { + pub fn is_restart(&self) -> bool { + match self { + OnPanic::None => false, + OnPanic::Restart => true, + OnPanic::Requests(_) => false, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum KernelCommand { + StartProcess { + id: ProcessId, + wasm_bytes_handle: u128, + on_panic: OnPanic, + initial_capabilities: HashSet, + public: bool, + }, + KillProcess(ProcessId), // this is extrajudicial killing: we might lose messages! + // kernel only + RebootProcess { + process_id: ProcessId, + persisted: PersistedProcess, + }, + Shutdown, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum KernelResponse { + StartedProcess, + StartProcessError, + KilledProcess(ProcessId), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PersistedProcess { + pub wasm_bytes_handle: u128, + // pub drive: String, + // pub full_path: String, + pub on_panic: OnPanic, + pub capabilities: HashSet, + pub public: bool, // marks if a process allows messages from any process +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct VfsRequest { + pub drive: String, + pub action: VfsAction, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum VfsAction { + New, + Add { + full_path: String, + entry_type: AddEntryType, + }, + Rename { + full_path: String, + new_full_path: String, + }, + Delete(String), + WriteOffset { + full_path: String, + offset: u64, + }, + SetSize { + full_path: String, + size: u64, + }, + GetPath(u128), + GetHash(String), + GetEntry(String), + GetFileChunk { + full_path: String, + offset: u64, + length: u64, + }, + GetEntryLength(String), +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum AddEntryType { + Dir, + NewFile, // add a new file to fs and add name in vfs + ExistingFile { hash: u128 }, // link an existing file in fs to a new name in vfs + ZipArchive, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum GetEntryType { + Dir, + File, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum VfsResponse { + Ok, + Err(VfsError), + GetPath(Option), + GetHash(Option), + GetEntry { + // file bytes in payload, if entry was a file + is_file: bool, + children: Vec, + }, + GetFileChunk, // chunk in payload + GetEntryLength(u64), +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum VfsError { + BadDriveName, + BadDescriptor, + NoCap, + EntryNotFound, +} + +#[allow(dead_code)] +impl VfsError { + pub fn kind(&self) -> &str { + match *self { + VfsError::BadDriveName => "BadDriveName", + VfsError::BadDescriptor => "BadDescriptor", + VfsError::NoCap => "NoCap", + VfsError::EntryNotFound => "EntryNotFound", + } + } +} + +// +// package types +// + +pub type PackageVersion = (u32, u32, u32); + +/// the type that gets deserialized from `metadata.json` in a package +#[derive(Debug, Serialize, Deserialize)] +pub struct PackageMetadata { + pub package: String, + pub publisher: String, + pub version: PackageVersion, + pub description: Option, + pub website: Option, +} + +/// the type that gets deserialized from each entry in the array in `manifest.json` +#[derive(Debug, Serialize, Deserialize)] +pub struct PackageManifestEntry { + pub process_name: String, + pub process_wasm_path: String, + pub on_panic: OnPanic, + pub request_networking: bool, + pub request_messaging: Vec, + pub public: bool, +} + +// +// conversions between wit types and kernel types (annoying!) +// + +pub fn de_wit_request(wit: wit::Request) -> Request { + Request { + inherit: wit.inherit, + expects_response: wit.expects_response, + ipc: wit.ipc, + metadata: wit.metadata, + } +} + +pub fn en_wit_request(request: Request) -> wit::Request { + wit::Request { + inherit: request.inherit, + expects_response: request.expects_response, + ipc: request.ipc, + metadata: request.metadata, + } +} + +pub fn de_wit_response(wit: wit::Response) -> Response { + Response { + inherit: wit.inherit, + ipc: wit.ipc, + metadata: wit.metadata, + } +} + +pub fn en_wit_response(response: Response) -> wit::Response { + wit::Response { + inherit: response.inherit, + ipc: response.ipc, + metadata: response.metadata, + } +} + +pub fn de_wit_payload(wit: Option) -> Option { + match wit { + None => None, + Some(wit) => Some(Payload { + mime: wit.mime, + bytes: wit.bytes, + }), + } +} + +pub fn en_wit_payload(load: Option) -> Option { + match load { + None => None, + Some(load) => Some(wit::Payload { + mime: load.mime, + bytes: load.bytes, + }), + } +} + +pub fn de_wit_signed_capability(wit: wit::SignedCapability) -> SignedCapability { + SignedCapability { + issuer: Address { + node: wit.issuer.node, + process: ProcessId { + process_name: wit.issuer.process.process_name, + package_name: wit.issuer.process.package_name, + publisher_node: wit.issuer.process.publisher_node, + }, + }, + params: wit.params, + signature: wit.signature, + } +} + +pub fn en_wit_signed_capability(cap: SignedCapability) -> wit::SignedCapability { + wit::SignedCapability { + issuer: cap.issuer.en_wit(), + params: cap.params, + signature: cap.signature, + } +} diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index 43e6d9f8..7de8e183 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -1,13 +1,17 @@ +#![allow(dead_code)] + +use crate::component::uq_process::api::*; /// Uqbar process standard library for Rust compiled to WASM /// Must be used in context of bindings generated by uqbar.wit use serde::{Deserialize, Serialize}; -use crate::component::uq_process::api::*; wit_bindgen::generate!({ path: "../wit", world: "uq-process-lib", }); +pub mod kernel_types; + /// Override the println! macro to print to the terminal // macro_rules! println { // () => { @@ -233,113 +237,71 @@ pub enum AddressParseError { /// For payloads, we use bincode to serialize and deserialize to bytes. /// -pub fn send_typed_request( +pub fn send_typed_request( target: &Address, inherit_payload_and_context: bool, ipc: &T1, - metadata: Option<&T2>, - context: Option<&T3>, - payload: Option<&T4>, + metadata: Option, + payload: Option<&Payload>, timeout: Option, ) -> anyhow::Result<()> where T1: serde::Serialize, - T2: serde::Serialize, - T3: serde::Serialize, - T4: serde::Serialize, { - let payload = match payload { - Some(payload) => Some(Payload { - mime: None, - bytes: bincode::serialize(payload)?, - }), - None => None, - }; - let context = match context { - Some(context) => Some(serde_json::to_vec(context)?), - None => None, - }; crate::send_request( target, &Request { inherit: inherit_payload_and_context, expects_response: timeout, ipc: serde_json::to_vec(ipc)?, - metadata: match metadata { - Some(metadata) => Some(serde_json::to_string(metadata)?), - None => None, - }, + metadata, }, - context.as_ref(), - payload.as_ref(), + None, + payload, ); Ok(()) } -pub fn send_typed_response( +pub fn send_typed_response( inherit_payload: bool, ipc: &T1, - metadata: Option<&T2>, - payload: Option<&T3>, // will overwrite inherit flag if both are set + metadata: Option, + payload: Option<&Payload>, // will overwrite inherit flag if both are set ) -> anyhow::Result<()> where T1: serde::Serialize, - T2: serde::Serialize, - T3: serde::Serialize, { - let payload = match payload { - Some(payload) => Some(Payload { - mime: None, - bytes: bincode::serialize(payload)?, - }), - None => None, - }; crate::send_response( &Response { inherit: inherit_payload, ipc: serde_json::to_vec(ipc)?, - metadata: match metadata { - Some(metadata) => Some(serde_json::to_string(metadata)?), - None => None, - }, + metadata, }, - payload.as_ref(), + payload, ); Ok(()) } -pub fn send_and_await_typed_response( +pub fn send_and_await_typed_response( target: &Address, inherit_payload_and_context: bool, ipc: &T1, - metadata: Option<&T2>, - payload: Option<&T3>, + metadata: Option, + payload: Option<&Payload>, timeout: u64, ) -> anyhow::Result> where T1: serde::Serialize, - T2: serde::Serialize, - T3: serde::Serialize, { - let payload = match payload { - Some(payload) => Some(Payload { - mime: None, - bytes: bincode::serialize(payload)?, - }), - None => None, - }; let res = crate::send_and_await_response( target, &Request { inherit: inherit_payload_and_context, expects_response: Some(timeout), ipc: serde_json::to_vec(ipc)?, - metadata: match metadata { - Some(metadata) => Some(serde_json::to_string(metadata)?), - None => None, - }, + metadata, }, - payload.as_ref(), + payload, ); Ok(res) } @@ -373,12 +335,11 @@ where } pub fn grant_messaging(our: &Address, grant_to: &Vec) -> anyhow::Result<()> { - let Some(our_messaging_cap) = crate::get_capability( - our, - &"\"messaging\"".into() - ) else { + let Some(our_messaging_cap) = crate::get_capability(our, &"\"messaging\"".into()) else { // the kernel will always give us this capability, so this should never happen - return Err(anyhow::anyhow!("failed to get our own messaging capability!")) + return Err(anyhow::anyhow!( + "failed to get our own messaging capability!" + )); }; for process in grant_to { crate::share_capability(&process, &our_messaging_cap); diff --git a/src/main.rs b/src/main.rs index 8d101ad9..0b38c532 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use crate::types::*; use anyhow::Result; use dotenv; use ethers::prelude::namehash; @@ -6,8 +7,6 @@ use std::sync::Arc; use tokio::sync::{mpsc, oneshot}; use tokio::{fs, time::timeout}; -use crate::types::*; - mod encryptor; mod eth_rpc; mod filesystem; From 56be17145ace208751fe4b628e80acfee1b766f4 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 2 Nov 2023 23:43:10 -0400 Subject: [PATCH 07/40] gorgeous new wit 0.3.0, WIP terminal only --- Cargo.lock | 151 ++++++++- Cargo.toml | 1 + build.rs | 4 +- modules/app_store/app_store/Cargo.lock | 1 + modules/terminal/Cargo.lock | 66 ++++ modules/terminal/Cargo.toml | 4 +- modules/terminal/src/lib.rs | 4 +- process_lib/Cargo.lock | 312 +++++++++++++++++++ process_lib/Cargo.toml | 1 + process_lib/src/kernel_types.rs | 102 +++++- process_lib/src/lib.rs | 7 +- src/kernel/mod.rs | 66 ++-- src/kernel/utils.rs | 118 ------- src/types.rs | 412 ++++++++++++++++--------- wit/uqbar.wit | 34 +- 15 files changed, 944 insertions(+), 339 deletions(-) create mode 100644 process_lib/Cargo.lock delete mode 100644 src/kernel/utils.rs diff --git a/Cargo.lock b/Cargo.lock index c9e8c7fe..dc9b26ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2023,6 +2023,9 @@ name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "hermit-abi" @@ -4207,6 +4210,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "spdx" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" +dependencies = [ + "smallvec 1.11.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -4802,6 +4814,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.10" @@ -4882,6 +4900,7 @@ dependencies = [ "thiserror", "tokio", "tokio-tungstenite 0.20.0", + "uqbar_process_lib", "url", "uuid 1.4.1", "walkdir", @@ -4891,6 +4910,18 @@ dependencies = [ "zip", ] +[[package]] +name = "uqbar_process_lib" +version = "0.2.0" +dependencies = [ + "anyhow", + "bincode", + "rand", + "serde", + "serde_json", + "wit-bindgen", +] + [[package]] name = "url" version = "2.4.1" @@ -5124,6 +5155,31 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" +dependencies = [ + "anyhow", + "indexmap 2.0.0", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder 0.36.1", + "wasmparser 0.116.0", +] + [[package]] name = "wasmparser" version = "0.110.0" @@ -5144,6 +5200,16 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" +dependencies = [ + "indexmap 2.0.0", + "semver", +] + [[package]] name = "wasmprinter" version = "0.2.64" @@ -5234,7 +5300,7 @@ dependencies = [ "syn 2.0.32", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser", + "wit-parser 0.9.2", ] [[package]] @@ -5477,7 +5543,7 @@ dependencies = [ "anyhow", "heck", "indexmap 2.0.0", - "wit-parser", + "wit-parser 0.9.2", ] [[package]] @@ -5728,6 +5794,70 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "wit-bindgen" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "bitflags 2.4.0", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "anyhow", + "wit-component", + "wit-parser 0.12.2", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "anyhow", + "heck", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 2.0.32", + "wit-bindgen-core", + "wit-bindgen-rust", + "wit-component", +] + +[[package]] +name = "wit-component" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" +dependencies = [ + "anyhow", + "bitflags 2.4.0", + "indexmap 2.0.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.36.1", + "wasm-metadata", + "wasmparser 0.116.0", + "wit-parser 0.12.2", +] + [[package]] name = "wit-parser" version = "0.9.2" @@ -5744,6 +5874,23 @@ dependencies = [ "url", ] +[[package]] +name = "wit-parser" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.0.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] + [[package]] name = "witx" version = "0.9.1" diff --git a/Cargo.toml b/Cargo.toml index 733d3121..0e80f3e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ thiserror = "1.0.43" tokio = { version = "1.28", features = ["fs", "macros", "rt-multi-thread", "sync"] } tokio-tungstenite = "*" url = "*" +uqbar_process_lib = { path = "process_lib" } uuid = { version = "1.1.2", features = ["serde", "v4"] } warp = "0.3.5" wasmtime = "12.0.1" diff --git a/build.rs b/build.rs index afe98911..10f49edb 100644 --- a/build.rs +++ b/build.rs @@ -109,7 +109,7 @@ fn build_app(target_path: &str, name: &str, parent_pkg_path: Option<&str>) { "embed", "wit", "--world", - "uq-process", + "process", &format!( "{}/target/wasm32-wasi/release/{}_adapted.wasm", target_path, name @@ -153,7 +153,7 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if package_name != "terminal" && package_name != "app_store" { + if package_name != "terminal" { continue; } diff --git a/modules/app_store/app_store/Cargo.lock b/modules/app_store/app_store/Cargo.lock index a34d6800..593f2a85 100644 --- a/modules/app_store/app_store/Cargo.lock +++ b/modules/app_store/app_store/Cargo.lock @@ -328,6 +328,7 @@ version = "0.2.0" dependencies = [ "anyhow", "bincode", + "rand", "serde", "serde_json", "wit-bindgen", diff --git a/modules/terminal/Cargo.lock b/modules/terminal/Cargo.lock index c05a6991..53004782 100644 --- a/modules/terminal/Cargo.lock +++ b/modules/terminal/Cargo.lock @@ -23,12 +23,29 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.14.2" @@ -73,12 +90,24 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.69" @@ -97,6 +126,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "ryu" version = "1.0.15" @@ -202,11 +261,18 @@ version = "0.2.0" dependencies = [ "anyhow", "bincode", + "rand", "serde", "serde_json", "wit-bindgen", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-encoder" version = "0.36.1" diff --git a/modules/terminal/Cargo.toml b/modules/terminal/Cargo.toml index 46dcc666..63328db3 100644 --- a/modules/terminal/Cargo.toml +++ b/modules/terminal/Cargo.toml @@ -22,9 +22,7 @@ uqbar_process_lib = { path = "../../process_lib" } crate-type = ["cdylib"] [package.metadata.component] -package = "component:uq-process" +package = "uqbar:process" [package.metadata.component.target] path = "wit" - -[package.metadata.component.dependencies] diff --git a/modules/terminal/src/lib.rs b/modules/terminal/src/lib.rs index 9c00d2b5..48560896 100644 --- a/modules/terminal/src/lib.rs +++ b/modules/terminal/src/lib.rs @@ -1,8 +1,8 @@ -use uqbar_process_lib::component::uq_process::api::*; +use uqbar_process_lib::uqbar::process::standard::*; wit_bindgen::generate!({ path: "../../wit", - world: "uq-process", + world: "process", exports: { world: Component, }, diff --git a/process_lib/Cargo.lock b/process_lib/Cargo.lock new file mode 100644 index 00000000..56088d02 --- /dev/null +++ b/process_lib/Cargo.lock @@ -0,0 +1,312 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "spdx" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" +dependencies = [ + "smallvec", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "uqbar_process_lib" +version = "0.2.0" +dependencies = [ + "anyhow", + "bincode", + "serde", + "serde_json", + "wit-bindgen", +] + +[[package]] +name = "wasm-encoder" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" +dependencies = [ + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "bitflags", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "anyhow", + "wit-component", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "anyhow", + "heck", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", + "wit-component", +] + +[[package]] +name = "wit-component" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] diff --git a/process_lib/Cargo.toml b/process_lib/Cargo.toml index 927ff791..afbcf1b6 100644 --- a/process_lib/Cargo.toml +++ b/process_lib/Cargo.toml @@ -8,4 +8,5 @@ anyhow = "1.0.71" bincode = "1.3.3" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" +rand = "0.8" wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } diff --git a/process_lib/src/kernel_types.rs b/process_lib/src/kernel_types.rs index 9df17865..d71da7c7 100644 --- a/process_lib/src/kernel_types.rs +++ b/process_lib/src/kernel_types.rs @@ -1,4 +1,4 @@ -use crate::component::uq_process::api as wit; +use crate::uqbar::process::standard as wit; use serde::{Deserialize, Serialize}; use std::collections::HashSet; @@ -26,9 +26,11 @@ pub struct ProcessId { #[allow(dead_code)] impl ProcessId { /// generates a random u64 number if process_name is not declared - pub fn new(process_name: &str, package_name: &str, publisher_node: &str) -> Self { + pub fn new(process_name: Option<&str>, package_name: &str, publisher_node: &str) -> Self { ProcessId { - process_name: process_name.into(), + process_name: process_name + .unwrap_or(&rand::random::().to_string()) + .into(), package_name: package_name.into(), publisher_node: publisher_node.into(), } @@ -340,6 +342,58 @@ pub struct PackageManifestEntry { pub public: bool, } +// +// display impls +// + +impl std::fmt::Display for ProcessId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +impl std::fmt::Display for Address { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}@{}", self.node, self.process.to_string(),) + } +} + +impl std::fmt::Display for Message { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Message::Request(request) => write!( + f, + "Request(\n inherit: {},\n expects_response: {:?},\n ipc: {},\n metadata: {}\n )", + request.inherit, + request.expects_response, + match serde_json::from_slice::(&request.ipc) { + Ok(json) => format!("{}", json), + Err(_) => format!("{:?}", request.ipc), + }, + &request.metadata.as_ref().unwrap_or(&"None".into()), + ), + Message::Response((response, context)) => write!( + f, + "Response(\n inherit: {},\n ipc: {},\n metadata: {},\n context: {}\n )", + response.inherit, + match serde_json::from_slice::(&response.ipc) { + Ok(json) => format!("{}", json), + Err(_) => format!("{:?}", response.ipc), + }, + &response.metadata.as_ref().unwrap_or(&"None".into()), + if context.is_none() { + "None".into() + } else { + match serde_json::from_slice::(&context.as_ref().unwrap()) { + Ok(json) => format!("{}", json), + Err(_) => format!("{:?}", context.as_ref().unwrap()), + } + }, + ), + } + } +} + // // conversions between wit types and kernel types (annoying!) // @@ -420,3 +474,45 @@ pub fn en_wit_signed_capability(cap: SignedCapability) -> wit::SignedCapability signature: cap.signature, } } + +pub fn en_wit_message(message: Message) -> wit::Message { + match message { + Message::Request(request) => wit::Message::Request(en_wit_request(request)), + Message::Response((response, context)) => { + wit::Message::Response((en_wit_response(response), context)) + } + } +} + +pub fn en_wit_send_error(error: SendError) -> wit::SendError { + wit::SendError { + kind: en_wit_send_error_kind(error.kind), + message: en_wit_message(error.message), + payload: en_wit_payload(error.payload), + } +} + +pub fn en_wit_send_error_kind(kind: SendErrorKind) -> wit::SendErrorKind { + match kind { + SendErrorKind::Offline => wit::SendErrorKind::Offline, + SendErrorKind::Timeout => wit::SendErrorKind::Timeout, + } +} + +pub fn de_wit_on_panic(wit: wit::OnPanic) -> OnPanic { + match wit { + wit::OnPanic::None => OnPanic::None, + wit::OnPanic::Restart => OnPanic::Restart, + wit::OnPanic::Requests(reqs) => OnPanic::Requests( + reqs.into_iter() + .map(|(address, request, payload)| { + ( + Address::de_wit(address), + de_wit_request(request), + de_wit_payload(payload), + ) + }) + .collect(), + ), + } +} diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index 7de8e183..d39d846b 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -1,18 +1,18 @@ #![allow(dead_code)] - -use crate::component::uq_process::api::*; +use crate::uqbar::process::standard::*; /// Uqbar process standard library for Rust compiled to WASM /// Must be used in context of bindings generated by uqbar.wit use serde::{Deserialize, Serialize}; wit_bindgen::generate!({ path: "../wit", - world: "uq-process-lib", + world: "lib", }); pub mod kernel_types; /// Override the println! macro to print to the terminal +/// TODO make this work // macro_rules! println { // () => { // $print_to_terminal(0, "\n"); @@ -357,7 +357,6 @@ pub fn can_message(address: &Address) -> bool { /// and other components -- if you have the capability to do so. /// -// move these to better place! #[derive(Serialize, Deserialize, Debug)] pub enum FsAction { Write, diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index a4440668..f4f5fc9e 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1,3 +1,8 @@ +use crate::kernel::uqbar::process::standard as wit; +use crate::types as t; +use crate::FILESYSTEM_PROCESS_ID; +use crate::KERNEL_PROCESS_ID; +use crate::VFS_PROCESS_ID; use anyhow::Result; use ring::signature::{self, KeyPair}; use serde::{Deserialize, Serialize}; @@ -10,28 +15,14 @@ use std::sync::{ }; use tokio::sync::mpsc; use tokio::task::JoinHandle; +use uqbar::process::standard::Host as StandardHost; use wasmtime::component::*; use wasmtime::{Config, Engine, Store, WasmBacktraceDetails}; use wasmtime_wasi::preview2::{Table, WasiCtx, WasiCtxBuilder, WasiView}; -use crate::types as t; -use crate::FILESYSTEM_PROCESS_ID; -use crate::KERNEL_PROCESS_ID; -use crate::VFS_PROCESS_ID; -// WIT errors when `use`ing interface unless we import this and implement Host for Process below -use crate::kernel::component::uq_process::types as wit; -use crate::kernel::wit::Host; -// use crate::kernel::component::uq_process::types::Host; -// use crate::kernel::component::uq_process::api::Host; - -use component::uq_process::api::Host as Foo; - -mod utils; -use crate::kernel::utils::*; - bindgen!({ path: "wit", - world: "uq-process", + world: "process", async: true, }); @@ -42,7 +33,7 @@ type ProcessMessageSender = type ProcessMessageReceiver = tokio::sync::mpsc::Receiver>; -struct Process { +struct ProcessState { keypair: Arc, metadata: t::ProcessMetadata, recv_in_process: ProcessMessageReceiver, @@ -58,7 +49,7 @@ struct Process { } struct ProcessWasi { - process: Process, + process: ProcessState, table: Table, wasi: WasiCtx, } @@ -81,7 +72,7 @@ enum ProcessSender { Userspace(ProcessMessageSender), } -impl Host for ProcessWasi {} +//impl Host for ProcessWasi {} impl WasiView for ProcessWasi { fn table(&self) -> &Table { @@ -102,7 +93,7 @@ impl WasiView for ProcessWasi { /// create the process API. this is where the functions that a process can use live. /// #[async_trait::async_trait] -impl Foo for ProcessWasi { +impl StandardHost for ProcessWasi { // // system utils: // @@ -129,7 +120,7 @@ impl Foo for ProcessWasi { /// TODO critical: move to kernel logic to enable persistence of choice made here async fn set_on_panic(&mut self, on_panic: wit::OnPanic) -> Result<()> { - self.process.metadata.on_panic = de_wit_on_panic(on_panic); + self.process.metadata.on_panic = t::de_wit_on_panic(on_panic); Ok(()) } @@ -360,7 +351,7 @@ impl Foo for ProcessWasi { ipc: serde_json::to_vec(&t::KernelCommand::StartProcess { id: new_process_id.clone(), wasm_bytes_handle: hash, - on_panic: de_wit_on_panic(on_panic), + on_panic: t::de_wit_on_panic(on_panic), // TODO initial_capabilities: match capabilities { wit::Capabilities::None => HashSet::new(), @@ -526,11 +517,12 @@ impl Foo for ProcessWasi { async fn attach_capability(&mut self, capability: wit::SignedCapability) -> Result<()> { match self.process.next_message_caps { None => { - self.process.next_message_caps = Some(vec![de_wit_signed_capability(capability)]); + self.process.next_message_caps = + Some(vec![t::de_wit_signed_capability(capability)]); Ok(()) } Some(ref mut v) => { - v.push(de_wit_signed_capability(capability)); + v.push(t::de_wit_signed_capability(capability)); Ok(()) } } @@ -664,7 +656,7 @@ impl Foo for ProcessWasi { /// if the prompting message did not have a payload, will return None. /// will also return None if there is no prompting message. async fn get_payload(&mut self) -> Result> { - Ok(en_wit_payload(self.process.last_payload.clone())) + Ok(t::en_wit_payload(self.process.last_payload.clone())) } async fn send_request( @@ -756,7 +748,7 @@ async fn send_and_await_response( } } -impl Process { +impl ProcessState { /// save a context for a given request. async fn save_context( &mut self, @@ -849,11 +841,11 @@ impl Process { } }, Err(e) => match self.contexts.remove(&e.id) { - None => return Err((en_wit_send_error(e.error), None)), + None => return Err((t::en_wit_send_error(e.error), None)), Some((context, timeout_handle)) => { timeout_handle.abort(); self.prompting_message = context.prompting_message; - return Err((en_wit_send_error(e.error), context.context)); + return Err((t::en_wit_send_error(e.error), context.context)); } }, }; @@ -864,9 +856,9 @@ impl Process { Ok(( km.source.en_wit().to_owned(), match km.message { - t::Message::Request(request) => wit::Message::Request(en_wit_request(request)), + t::Message::Request(request) => wit::Message::Request(t::en_wit_request(request)), t::Message::Response((response, _context)) => { - wit::Message::Response((en_wit_response(response), context)) + wit::Message::Response((t::en_wit_response(response), context)) } }, )) @@ -963,7 +955,7 @@ impl Process { // no rsvp because neither prompting message nor this request wants a response (_, None, None) => None, }, - message: t::Message::Request(de_wit_request(request.clone())), + message: t::Message::Request(t::de_wit_request(request.clone())), payload: payload.clone(), signed_capabilities: None, }; @@ -983,7 +975,7 @@ impl Process { error: t::SendError { kind: t::SendErrorKind::Timeout, target: t::Address::de_wit(target), - message: t::Message::Request(de_wit_request(request.clone())), + message: t::Message::Request(t::de_wit_request(request.clone())), payload, }, })) @@ -1019,7 +1011,7 @@ impl Process { let payload = match response.inherit { true => self.last_payload.clone(), - false => de_wit_payload(payload), + false => t::de_wit_payload(payload), }; self.send_to_loop @@ -1029,7 +1021,7 @@ impl Process { target, rsvp: None, message: t::Message::Response(( - de_wit_response(response), + t::de_wit_response(response), // the context will be set by the process receiving this Response. None, )), @@ -1119,7 +1111,7 @@ async fn make_process_loop( Component::new(&engine, wasm_bytes).expect("make_process_loop: couldn't read file"); let mut linker = Linker::new(&engine); - UqProcess::add_to_linker(&mut linker, |state: &mut ProcessWasi| state).unwrap(); + Process::add_to_linker(&mut linker, |state: &mut ProcessWasi| state).unwrap(); let mut table = Table::new(); let wasi = WasiCtxBuilder::new().build(&mut table).unwrap(); @@ -1147,7 +1139,7 @@ async fn make_process_loop( let mut store = Store::new( engine, ProcessWasi { - process: Process { + process: ProcessState { keypair: keypair.clone(), metadata: metadata.clone(), recv_in_process, @@ -1168,7 +1160,7 @@ async fn make_process_loop( Box::pin(async move { let (bindings, _bindings) = - match UqProcess::instantiate_async(&mut store, &component, &linker).await { + match Process::instantiate_async(&mut store, &component, &linker).await { Ok(b) => b, Err(e) => { let _ = send_to_terminal diff --git a/src/kernel/utils.rs b/src/kernel/utils.rs deleted file mode 100644 index 0587b2f5..00000000 --- a/src/kernel/utils.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::kernel::component::uq_process::types as wit; -use crate::types as t; - -// -// conversions between wit types and kernel types (annoying!) -// - -pub fn en_wit_message(message: t::Message) -> wit::Message { - match message { - t::Message::Request(request) => wit::Message::Request(en_wit_request(request)), - t::Message::Response((response, context)) => { - wit::Message::Response((en_wit_response(response), context)) - } - } -} - -pub fn de_wit_request(wit: wit::Request) -> t::Request { - t::Request { - inherit: wit.inherit, - expects_response: wit.expects_response, - ipc: wit.ipc, - metadata: wit.metadata, - } -} - -pub fn en_wit_request(request: t::Request) -> wit::Request { - wit::Request { - inherit: request.inherit, - expects_response: request.expects_response, - ipc: request.ipc, - metadata: request.metadata, - } -} - -pub fn de_wit_response(wit: wit::Response) -> t::Response { - t::Response { - inherit: wit.inherit, - ipc: wit.ipc, - metadata: wit.metadata, - } -} - -pub fn en_wit_response(response: t::Response) -> wit::Response { - wit::Response { - inherit: response.inherit, - ipc: response.ipc, - metadata: response.metadata, - } -} - -pub fn en_wit_send_error(error: t::SendError) -> wit::SendError { - wit::SendError { - kind: en_wit_send_error_kind(error.kind), - message: en_wit_message(error.message), - payload: en_wit_payload(error.payload), - } -} - -pub fn en_wit_send_error_kind(kind: t::SendErrorKind) -> wit::SendErrorKind { - match kind { - t::SendErrorKind::Offline => wit::SendErrorKind::Offline, - t::SendErrorKind::Timeout => wit::SendErrorKind::Timeout, - } -} - -pub fn de_wit_payload(wit: Option) -> Option { - match wit { - None => None, - Some(wit) => Some(t::Payload { - mime: wit.mime, - bytes: wit.bytes, - }), - } -} - -pub fn en_wit_payload(payload: Option) -> Option { - match payload { - None => None, - Some(payload) => Some(wit::Payload { - mime: payload.mime, - bytes: payload.bytes, - }), - } -} - -pub fn de_wit_signed_capability(wit: wit::SignedCapability) -> t::SignedCapability { - t::SignedCapability { - issuer: t::Address::de_wit(wit.issuer), - params: wit.params, - signature: wit.signature, - } -} - -// pub fn en_wit_signed_capability(cap: t::SignedCapability) -> wit::SignedCapability { -// wit::SignedCapability { -// issuer: cap.issuer.en_wit().to_owned(), -// params: cap.params, -// signature: cap.signature, -// } -// } - -pub fn de_wit_on_panic(wit: wit::OnPanic) -> t::OnPanic { - match wit { - wit::OnPanic::None => t::OnPanic::None, - wit::OnPanic::Restart => t::OnPanic::Restart, - wit::OnPanic::Requests(reqs) => t::OnPanic::Requests( - reqs.into_iter() - .map(|(address, request, payload)| { - ( - t::Address::de_wit(address), - de_wit_request(request), - de_wit_payload(payload), - ) - }) - .collect(), - ), - } -} diff --git a/src/types.rs b/src/types.rs index 379f9b31..ac748d61 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -use crate::kernel::component::uq_process::types as wit; +use crate::kernel::uqbar::process::standard as wit; use ring::signature; use serde::{Deserialize, Serialize}; use std::{ @@ -20,95 +20,14 @@ lazy_static::lazy_static! { } // -// internal message pipes between kernel and runtime modules +// types shared between kernel and processes. frustratingly, this is an exact copy +// of the types in process_lib/src/kernel_types.rs +// this is because even though the types are identical, they will not match when +// used in the kernel context which generates bindings differently than the process +// standard library. make sure to keep this synced with kernel_types.rs // - -// keeps the from address so we know where to pipe error -pub type NetworkErrorSender = tokio::sync::mpsc::Sender; -pub type NetworkErrorReceiver = tokio::sync::mpsc::Receiver; - -pub type MessageSender = tokio::sync::mpsc::Sender; -pub type MessageReceiver = tokio::sync::mpsc::Receiver; - -pub type PrintSender = tokio::sync::mpsc::Sender; -pub type PrintReceiver = tokio::sync::mpsc::Receiver; - -pub type DebugSender = tokio::sync::mpsc::Sender; -pub type DebugReceiver = tokio::sync::mpsc::Receiver; - -pub type CapMessageSender = tokio::sync::mpsc::Sender; -pub type CapMessageReceiver = tokio::sync::mpsc::Receiver; - -// -// types used for UQI: uqbar's identity system -// -pub type NodeId = String; -pub type PKINames = Arc>>; // TODO maybe U256 to String -pub type OnchainPKI = Arc>>; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Registration { - pub username: NodeId, - pub password: String, - pub direct: bool, -} - -#[derive(Debug)] -pub struct Keyfile { - pub username: String, - pub routers: Vec, - pub networking_keypair: signature::Ed25519KeyPair, - pub jwt_secret_bytes: Vec, - pub file_key: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct KeyfileVet { - pub password: String, - pub keyfile: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct KeyfileVetted { - pub username: String, - pub networking_key: String, - pub routers: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BootInfo { - pub password: String, - pub keyfile: String, - pub username: String, - pub reset: bool, - pub direct: bool, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Identity { - pub name: NodeId, - pub networking_key: String, - pub ws_routing: Option<(String, u16)>, - pub allowed_routers: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdentityTransaction { - pub from: String, - pub signature: Option, - pub to: String, // contract address - pub town_id: u32, - pub calldata: Identity, - pub nonce: String, -} - -// -// process-facing kernel types, used for process -// management and message-passing -// matches types in uqbar.wit -// - pub type Context = Vec; +pub type NodeId = String; // QNS domain name /// process ID is a formatted unique identifier that contains /// the publishing node's ID, the package name, and finally the process name. @@ -127,10 +46,9 @@ impl ProcessId { /// generates a random u64 number if process_name is not declared pub fn new(process_name: Option<&str>, package_name: &str, publisher_node: &str) -> Self { ProcessId { - process_name: match process_name { - Some(name) => name.to_string(), - None => rand::random::().to_string(), - }, + process_name: process_name + .unwrap_or(&rand::random::().to_string()) + .into(), package_name: package_name.into(), publisher_node: publisher_node.into(), } @@ -266,7 +184,7 @@ pub struct SignedCapability { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SendError { pub kind: SendErrorKind, - pub target: Address, // what the message was trying to reach + pub target: Address, pub message: Message, pub payload: Option, } @@ -294,6 +212,266 @@ impl OnPanic { } } +// +// display impls +// + +impl std::fmt::Display for ProcessId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +impl std::fmt::Display for Address { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}@{}", self.node, self.process.to_string(),) + } +} + +impl std::fmt::Display for Message { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Message::Request(request) => write!( + f, + "Request(\n inherit: {},\n expects_response: {:?},\n ipc: {},\n metadata: {}\n )", + request.inherit, + request.expects_response, + match serde_json::from_slice::(&request.ipc) { + Ok(json) => format!("{}", json), + Err(_) => format!("{:?}", request.ipc), + }, + &request.metadata.as_ref().unwrap_or(&"None".into()), + ), + Message::Response((response, context)) => write!( + f, + "Response(\n inherit: {},\n ipc: {},\n metadata: {},\n context: {}\n )", + response.inherit, + match serde_json::from_slice::(&response.ipc) { + Ok(json) => format!("{}", json), + Err(_) => format!("{:?}", response.ipc), + }, + &response.metadata.as_ref().unwrap_or(&"None".into()), + if context.is_none() { + "None".into() + } else { + match serde_json::from_slice::(&context.as_ref().unwrap()) { + Ok(json) => format!("{}", json), + Err(_) => format!("{:?}", context.as_ref().unwrap()), + } + }, + ), + } + } +} + +// +// conversions between wit types and kernel types (annoying!) +// + +pub fn de_wit_request(wit: wit::Request) -> Request { + Request { + inherit: wit.inherit, + expects_response: wit.expects_response, + ipc: wit.ipc, + metadata: wit.metadata, + } +} + +pub fn en_wit_request(request: Request) -> wit::Request { + wit::Request { + inherit: request.inherit, + expects_response: request.expects_response, + ipc: request.ipc, + metadata: request.metadata, + } +} + +pub fn de_wit_response(wit: wit::Response) -> Response { + Response { + inherit: wit.inherit, + ipc: wit.ipc, + metadata: wit.metadata, + } +} + +pub fn en_wit_response(response: Response) -> wit::Response { + wit::Response { + inherit: response.inherit, + ipc: response.ipc, + metadata: response.metadata, + } +} + +pub fn de_wit_payload(wit: Option) -> Option { + match wit { + None => None, + Some(wit) => Some(Payload { + mime: wit.mime, + bytes: wit.bytes, + }), + } +} + +pub fn en_wit_payload(load: Option) -> Option { + match load { + None => None, + Some(load) => Some(wit::Payload { + mime: load.mime, + bytes: load.bytes, + }), + } +} + +pub fn de_wit_signed_capability(wit: wit::SignedCapability) -> SignedCapability { + SignedCapability { + issuer: Address { + node: wit.issuer.node, + process: ProcessId { + process_name: wit.issuer.process.process_name, + package_name: wit.issuer.process.package_name, + publisher_node: wit.issuer.process.publisher_node, + }, + }, + params: wit.params, + signature: wit.signature, + } +} + +pub fn _en_wit_signed_capability(cap: SignedCapability) -> wit::SignedCapability { + wit::SignedCapability { + issuer: cap.issuer.en_wit(), + params: cap.params, + signature: cap.signature, + } +} + +pub fn en_wit_message(message: Message) -> wit::Message { + match message { + Message::Request(request) => wit::Message::Request(en_wit_request(request)), + Message::Response((response, context)) => { + wit::Message::Response((en_wit_response(response), context)) + } + } +} + +pub fn en_wit_send_error(error: SendError) -> wit::SendError { + wit::SendError { + kind: en_wit_send_error_kind(error.kind), + message: en_wit_message(error.message), + payload: en_wit_payload(error.payload), + } +} + +pub fn en_wit_send_error_kind(kind: SendErrorKind) -> wit::SendErrorKind { + match kind { + SendErrorKind::Offline => wit::SendErrorKind::Offline, + SendErrorKind::Timeout => wit::SendErrorKind::Timeout, + } +} + +pub fn de_wit_on_panic(wit: wit::OnPanic) -> OnPanic { + match wit { + wit::OnPanic::None => OnPanic::None, + wit::OnPanic::Restart => OnPanic::Restart, + wit::OnPanic::Requests(reqs) => OnPanic::Requests( + reqs.into_iter() + .map(|(address, request, payload)| { + ( + Address::de_wit(address), + de_wit_request(request), + de_wit_payload(payload), + ) + }) + .collect(), + ), + } +} +// +// END SYNC WITH kernel_types.rs +// + +// +// internal message pipes between kernel and runtime modules +// + +// keeps the from address so we know where to pipe error +pub type NetworkErrorSender = tokio::sync::mpsc::Sender; +pub type NetworkErrorReceiver = tokio::sync::mpsc::Receiver; + +pub type MessageSender = tokio::sync::mpsc::Sender; +pub type MessageReceiver = tokio::sync::mpsc::Receiver; + +pub type PrintSender = tokio::sync::mpsc::Sender; +pub type PrintReceiver = tokio::sync::mpsc::Receiver; + +pub type DebugSender = tokio::sync::mpsc::Sender; +pub type DebugReceiver = tokio::sync::mpsc::Receiver; + +pub type CapMessageSender = tokio::sync::mpsc::Sender; +pub type CapMessageReceiver = tokio::sync::mpsc::Receiver; + +// +// types used for UQI: uqbar's identity system +// +pub type PKINames = Arc>>; // TODO maybe U256 to String +pub type OnchainPKI = Arc>>; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Registration { + pub username: NodeId, + pub password: String, + pub direct: bool, +} + +#[derive(Debug)] +pub struct Keyfile { + pub username: String, + pub routers: Vec, + pub networking_keypair: signature::Ed25519KeyPair, + pub jwt_secret_bytes: Vec, + pub file_key: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct KeyfileVet { + pub password: String, + pub keyfile: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct KeyfileVetted { + pub username: String, + pub networking_key: String, + pub routers: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BootInfo { + pub password: String, + pub keyfile: String, + pub username: String, + pub reset: bool, + pub direct: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Identity { + pub name: NodeId, + pub networking_key: String, + pub ws_routing: Option<(String, u16)>, + pub allowed_routers: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdentityTransaction { + pub from: String, + pub signature: Option, + pub to: String, // contract address + pub town_id: u32, + pub calldata: Identity, + pub nonce: String, +} + // // kernel types that runtime modules use // @@ -713,18 +891,6 @@ impl HttpClientError { // custom kernel displays // -impl std::fmt::Display for ProcessId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.to_string()) - } -} - -impl std::fmt::Display for Address { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}@{}", self.node, self.process.to_string(),) - } -} - impl std::fmt::Display for KernelMessage { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( @@ -743,42 +909,6 @@ impl std::fmt::Display for KernelMessage { } } -impl std::fmt::Display for Message { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Message::Request(request) => write!( - f, - "Request(\n inherit: {},\n expects_response: {:?},\n ipc: {},\n metadata: {}\n )", - request.inherit, - request.expects_response, - match serde_json::from_slice::(&request.ipc) { - Ok(json) => format!("{}", json), - Err(_) => format!("{:?}", request.ipc), - }, - &request.metadata.as_ref().unwrap_or(&"None".into()), - ), - Message::Response((response, context)) => write!( - f, - "Response(\n inherit: {},\n ipc: {},\n metadata: {},\n context: {}\n )", - response.inherit, - match serde_json::from_slice::(&response.ipc) { - Ok(json) => format!("{}", json), - Err(_) => format!("{:?}", response.ipc), - }, - &response.metadata.as_ref().unwrap_or(&"None".into()), - if context.is_none() { - "None".into() - } else { - match serde_json::from_slice::(&context.as_ref().unwrap()) { - Ok(json) => format!("{}", json), - Err(_) => format!("{:?}", context.as_ref().unwrap()), - } - }, - ), - } - } -} - // // http_server.rs types // diff --git a/wit/uqbar.wit b/wit/uqbar.wit index e785ebb1..821848db 100644 --- a/wit/uqbar.wit +++ b/wit/uqbar.wit @@ -1,12 +1,12 @@ -package component:uq-process@0.2.0 +package uqbar:process@0.3.0 -interface types { +interface standard { // JSON is passed over WASM boundary as a string. type json = string type node-id = string - // context, like ipc, is a protocol-defined cap'n proto schema + // context, like ipc, is a protocol-defined serialized byte array. // it is used when building a Request to save information // that will not be part of a Response, in order to more // easily handle ("contextualize") that Response. @@ -100,26 +100,6 @@ interface types { no-file-at-path, // TODO more here? } -} - -interface api { - use types.{ - json, - node-id, - context, - process-id, - address, - payload, - request, - response, - message, - capabilities, - signed-capability, - on-panic, - send-error, - send-error-kind, - spawn-error, - } // system utils: print-to-terminal: func(verbosity: u8, message: string) @@ -195,12 +175,12 @@ interface api { result, send-error> } -world uq-process-lib { - import api +world lib { + import standard } -world uq-process { - include uq-process-lib +world process { + include lib export init: func(our: string) } From 679c4b975c81817f9b1e0b77373f618caa666cb2 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Sun, 5 Nov 2023 23:03:06 -0500 Subject: [PATCH 08/40] println macro replace working, fixing up other apps --- modules/app_store/app_store/src/lib.rs | 9 +- modules/qns_indexer/Cargo.toml | 20 +- modules/qns_indexer/src/lib.rs | 402 +++++++++++------------- modules/qns_indexer/src/process_lib.rs | 1 - modules/terminal/src/lib.rs | 111 +++---- process_lib/src/lib.rs | 418 +++++++++++++++++++------ src/process_lib.rs | 307 ------------------ 7 files changed, 570 insertions(+), 698 deletions(-) delete mode 120000 modules/qns_indexer/src/process_lib.rs delete mode 100644 src/process_lib.rs diff --git a/modules/app_store/app_store/src/lib.rs b/modules/app_store/app_store/src/lib.rs index c6202403..40db16b4 100644 --- a/modules/app_store/app_store/src/lib.rs +++ b/modules/app_store/app_store/src/lib.rs @@ -1,14 +1,13 @@ use serde::{Deserialize, Serialize}; use sha2::Digest; use std::collections::{HashMap, HashSet}; -use uqbar_process_lib::component::uq_process::api::*; -use uqbar_process_lib::component::uq_process::types::NodeId; -use uqbar_process_lib::*; use uqbar_process_lib::kernel_types as kt; +use uqbar_process_lib::uqbar::process::standard as wit; +use uqbar_process_lib::{NodeId, Address, ProcessId, Request}; wit_bindgen::generate!({ - path: "../../../wit", - world: "uq-process", + path: "../../wit", + world: "process", exports: { world: Component, }, diff --git a/modules/qns_indexer/Cargo.toml b/modules/qns_indexer/Cargo.toml index f9dbd05a..66d522a8 100644 --- a/modules/qns_indexer/Cargo.toml +++ b/modules/qns_indexer/Cargo.toml @@ -11,24 +11,18 @@ opt-level = "s" lto = true [dependencies] -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } -serde_json = "1.0" -serde = {version = "1.0", features = ["derive"] } -wit-bindgen = { version = "0.11.0", default_features = false } -thiserror = "1.0.43" anyhow = "1.0" -alloy-sol-types = "0.3.2" -hex = "0.4.3" alloy-primitives = "0.3.3" +alloy-sol-types = "0.3.2" bincode = "1.3.3" +hex = "0.4.3" +serde = {version = "1.0", features = ["derive"] } +serde_json = "1.0" +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +uqbar_process_lib = { path = "../../process_lib" } [lib] crate-type = ["cdylib"] [package.metadata.component] -package = "component:uq-process" - -[package.metadata.component.target] -path = "wit" - -[package.metadata.component.dependencies] +package = "uqbar:process" diff --git a/modules/qns_indexer/src/lib.rs b/modules/qns_indexer/src/lib.rs index 66265913..2ad5074d 100644 --- a/modules/qns_indexer/src/lib.rs +++ b/modules/qns_indexer/src/lib.rs @@ -1,17 +1,20 @@ -cargo_component_bindings::generate!(); - use alloy_primitives::FixedBytes; use alloy_sol_types::{sol, SolEvent}; -use bindings::component::uq_process::types::*; -use bindings::{print_to_terminal, receive, send_request, send_response, UqProcess}; use hex; use serde::{Deserialize, Serialize}; use serde_json::json; use std::collections::HashMap; use std::string::FromUtf8Error; +use uqbar_process_lib::uqbar::process::standard as wit; +use uqbar_process_lib::{Address, ProcessId, Request, Response}; -#[allow(dead_code)] -mod process_lib; +wit_bindgen::generate!({ + path: "../../wit", + world: "process", + exports: { + world: Component, + }, +}); struct Component; @@ -98,8 +101,26 @@ fn subscribe_to_qns(from_block: u64) -> Vec { .to_vec() } +fn serialize_state(state: &State) -> anyhow::Result> { + Ok(bincode::serialize(state)?) +} + +fn deserialize_state(bytes: &[u8]) -> anyhow::Result { + Ok(bincode::deserialize(bytes)?) +} + +fn serialize_message(message: &NetActions) -> anyhow::Result> { + Ok(serde_json::to_vec(message)?) +} + +fn deserialize_message(bytes: &[u8]) -> anyhow::Result { + Ok(serde_json::from_slice(bytes)?) +} + impl UqProcess for Component { - fn init(our: Address) { + fn init(our: String) { + let our = Address::from_str(&our).unwrap(); + let mut state: State = State { names: HashMap::new(), nodes: HashMap::new(), @@ -107,247 +128,202 @@ impl UqProcess for Component { }; // if we have state, load it in - match process_lib::get_state::() { + match get_typed_state::(deserialize_state) { Some(s) => { state = s; } None => {} } - bindings::print_to_terminal( + print_to_terminal( 0, &format!("qns_indexer: starting at block {}", state.block), ); - // shove all state into net::net - send_request( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("net:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: None, - metadata: None, - ipc: serde_json::to_vec(&NetActions::QnsBatchUpdate( - state.nodes.values().cloned().collect::>(), - )) - .unwrap(), - }, - None, - None, - ); + match main(our, state) { + Ok(_) => {} + Err(e) => { + print_to_terminal(0, &format!("qns_indexer: ended with error: {:?}", e)); + } + } + } +} - let _ = send_request( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("eth_rpc:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, // TODO what - expects_response: Some(5), // TODO evaluate - metadata: None, - // -1 because there could be other events in the last processed block - ipc: subscribe_to_qns(state.block - 1), - }, - None, - None, - ); +fn main(our: Address, state: State) -> anyhow::Result<()> { + // shove all state into net::net + Request::new() + .target(Address::new(our.node, "net:sys:uqbar")?)? + .ipc( + &NetActions::QnsBatchUpdate(state.nodes.values().cloned().collect::>()), + serialize_message, + )? + .send()?; - let http_server_address = ProcessId::from_str("http_server:sys:uqbar").unwrap(); + Request::new() + .target(Address::new(our.node, "eth_rpc:sys:uqbar")?)? + .ipc_bytes(subscribe_to_qns(state.block - 1))? + .expects_response(5) + .send()?; - let _register_endpoint = send_request( - &Address { - node: our.node.clone(), - process: http_server_address.clone(), - }, - &Request { - inherit: false, - expects_response: None, - metadata: None, - ipc: json!({ - "BindPath": { - "path": "/node/:name", - "authenticated": false, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - }, - None, - None, - ); + let http_server_address = ProcessId::from_str("http_server:sys:uqbar").unwrap(); - loop { - let Ok((source, message)) = receive() else { - print_to_terminal(0, "qns_indexer: got network error"); - continue; - }; - let Message::Request(request) = message else { - // TODO we should store the subscription ID for eth_rpc - // incase we want to cancel/reset it - // print_to_terminal(0, "qns_indexer: got response"); - continue; - }; + Request::new() + .target(Address::new(our.node, http_server_address)?)? + .ipc_bytes( + json!({ + "BindPath": { + "path": "/node/:name", + "authenticated": false, + "local_only": false + } + }) + .to_string() + .as_bytes(), + )? + .send()?; - if source.process == http_server_address { - if let Ok(ipc_json) = - serde_json::from_slice::(&request.ipc) - { - if ipc_json["path"].as_str().unwrap_or_default() == "/node/:name" { - if let Some(name) = ipc_json["url_params"]["name"].as_str() { - if let Some(node) = state.nodes.get(name) { - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "status": 200, - "headers": { - "Content-Type": "application/json", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { + loop { + let Ok((source, message)) = receive() else { + print_to_terminal(0, "qns_indexer: got network error"); + continue; + }; + let Message::Request(request) = message else { + // TODO we should store the subscription ID for eth_rpc + // incase we want to cancel/reset it + // print_to_terminal(0, "qns_indexer: got response"); + continue; + }; + + if source.process == http_server_address { + if let Ok(ipc_json) = serde_json::from_slice::(&request.ipc) { + if ipc_json["path"].as_str().unwrap_or_default() == "/node/:name" { + if let Some(name) = ipc_json["url_params"]["name"].as_str() { + if let Some(node) = state.nodes.get(name) { + Response::new() + .ipc_bytes( + serde_json::json!({ + "status": 200, + "headers": { + "Content-Type": "application/json", + }, + }) + .payload(&Payload { mime: Some("application/json".to_string()), bytes: serde_json::to_string(&node) .unwrap() .as_bytes() .to_vec(), - }), - ); - continue; - } + })?, + ) + .send()?; + continue; } } } - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "status": 404, - "headers": { - "Content-Type": "application/json", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { + } + Response::new() + .ipc_bytes( + serde_json::json!({ + "status": 404, + "headers": { + "Content-Type": "application/json", + }, + }) + .payload(&Payload { mime: Some("application/json".to_string()), bytes: "Not Found".to_string().as_bytes().to_vec(), - }), - ); - continue; - } + })?, + ) + .send()?; + continue; + } - let Ok(msg) = serde_json::from_slice::(&request.ipc) else { - print_to_terminal(0, "qns_indexer: got invalid message"); - continue; - }; + let Ok(msg) = serde_json::from_slice::(&request.ipc) else { + print_to_terminal(0, "qns_indexer: got invalid message"); + continue; + }; - match msg { - // Probably more message types later...maybe not... - AllActions::EventSubscription(e) => { - state.block = hex_to_u64(&e.block_number).unwrap(); - match decode_hex(&e.topics[0].clone()) { - NodeRegistered::SIGNATURE_HASH => { - // bindings::print_to_terminal(0, format!("qns_indexer: got NodeRegistered event: {:?}", e).as_str()); + match msg { + // Probably more message types later...maybe not... + AllActions::EventSubscription(e) => { + state.block = hex_to_u64(&e.block_number).unwrap(); + match decode_hex(&e.topics[0].clone()) { + NodeRegistered::SIGNATURE_HASH => { + // print_to_terminal(0, format!("qns_indexer: got NodeRegistered event: {:?}", e).as_str()); - let node = &e.topics[1]; - let decoded = - NodeRegistered::decode_data(&decode_hex_to_vec(&e.data), true) - .unwrap(); - let Ok(name) = dnswire_decode(decoded.0.clone()) else { - bindings::print_to_terminal( - 1, - &format!("qns_indexer: failed to decode name: {:?}", decoded.0), - ); - continue; - }; - - state.names.insert(node.to_string(), name); - } - WsChanged::SIGNATURE_HASH => { - let node = &e.topics[1]; - let decoded = - WsChanged::decode_data(&decode_hex_to_vec(&e.data), true).unwrap(); - let public_key = hex::encode(decoded.0); - let ip = decoded.1; - let port = decoded.2; - let routers_raw = decoded.3; - let routers: Vec = routers_raw - .iter() - .map(|r| { - let key = hex::encode(r); - match state.names.get(&key) { - Some(name) => name.clone(), - None => format!("0x{}", key), // TODO it should actually just panic here - } - }) - .collect::>(); - - let Some(name) = state.names.get(node) else { - bindings::print_to_terminal(0, &format!("qns_indexer: failed to find name for node during WsChanged: {:?}", node)); - continue; - }; - - let update = QnsUpdate { - name: name.clone(), - owner: "0x".to_string(), // TODO or get rid of - node: node.clone(), - public_key: format!("0x{}", public_key), - ip: format!( - "{}.{}.{}.{}", - (ip >> 24) & 0xFF, - (ip >> 16) & 0xFF, - (ip >> 8) & 0xFF, - ip & 0xFF - ), - port, - routers, - }; - - state.nodes.insert(name.clone(), update.clone()); - - send_request( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("net:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: None, - metadata: None, - ipc: serde_json::to_vec(&NetActions::QnsUpdate(update.clone())) - .unwrap(), - }, - None, - None, + let node = &e.topics[1]; + let decoded = + NodeRegistered::decode_data(&decode_hex_to_vec(&e.data), true).unwrap(); + let Ok(name) = dnswire_decode(decoded.0.clone()) else { + print_to_terminal( + 1, + &format!("qns_indexer: failed to decode name: {:?}", decoded.0), ); - } - event => { - bindings::print_to_terminal( - 0, - format!("qns_indexer: got unknown event: {:?}", event).as_str(), - ); - } + continue; + }; + + state.names.insert(node.to_string(), name); + } + WsChanged::SIGNATURE_HASH => { + let node = &e.topics[1]; + let decoded = + WsChanged::decode_data(&decode_hex_to_vec(&e.data), true).unwrap(); + let public_key = hex::encode(decoded.0); + let ip = decoded.1; + let port = decoded.2; + let routers_raw = decoded.3; + let routers: Vec = routers_raw + .iter() + .map(|r| { + let key = hex::encode(r); + match state.names.get(&key) { + Some(name) => name.clone(), + None => format!("0x{}", key), // TODO it should actually just panic here + } + }) + .collect::>(); + + let Some(name) = state.names.get(node) else { + print_to_terminal(0, &format!("qns_indexer: failed to find name for node during WsChanged: {:?}", node)); + continue; + }; + + let update = QnsUpdate { + name: name.clone(), + owner: "0x".to_string(), // TODO or get rid of + node: node.clone(), + public_key: format!("0x{}", public_key), + ip: format!( + "{}.{}.{}.{}", + (ip >> 24) & 0xFF, + (ip >> 16) & 0xFF, + (ip >> 8) & 0xFF, + ip & 0xFF + ), + port, + routers, + }; + + state.nodes.insert(name.clone(), update.clone()); + + Request::new() + .target(Address::new(our.node, "net:sys:uqbar")?)? + .ipc(&NetActions::QnsUpdate(update.clone()), serialize_message)? + .send()?; + } + event => { + print_to_terminal( + 0, + format!("qns_indexer: got unknown event: {:?}", event).as_str(), + ); } } } - - process_lib::set_state::(&state); } + + set_typed_state::(&state, serialize_state); } } - // helpers // TODO these probably exist somewhere in alloy...not sure where though. fn decode_hex(s: &str) -> FixedBytes<32> { diff --git a/modules/qns_indexer/src/process_lib.rs b/modules/qns_indexer/src/process_lib.rs deleted file mode 120000 index 77367fe0..00000000 --- a/modules/qns_indexer/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../src/process_lib.rs \ No newline at end of file diff --git a/modules/terminal/src/lib.rs b/modules/terminal/src/lib.rs index 48560896..255d4d8c 100644 --- a/modules/terminal/src/lib.rs +++ b/modules/terminal/src/lib.rs @@ -1,4 +1,6 @@ -use uqbar_process_lib::uqbar::process::standard::*; +use anyhow::anyhow; +use uqbar_process_lib::uqbar::process::standard as wit; +use uqbar_process_lib::{Address, ProcessId, Request, println}; wit_bindgen::generate!({ path: "../../wit", @@ -8,99 +10,65 @@ wit_bindgen::generate!({ }, }); -struct Component; +fn serialize_message(message: &&str) -> anyhow::Result> { + Ok(serde_json::to_vec(message)?) +} -fn parse_command(our_name: &str, line: &str) { +fn parse_command(our_name: &str, line: &str) -> anyhow::Result<()> { let (head, tail) = line.split_once(" ").unwrap_or((&line, "")); match head { - "" | " " => {} + "" | " " => return Ok(()), "!hi" => { - let (target, message) = match tail.split_once(" ") { + let (node_id, message) = match tail.split_once(" ") { Some((s, t)) => (s, t), - None => { - print_to_terminal(0, &format!("invalid command: \"{}\"", line)); - return; - } + None => return Err(anyhow!("invalid command: \"{line}\"")), }; - send_request( - &Address { - node: if target == "our" { - our_name.into() - } else { - target.into() - }, - process: ProcessId::from_str("net:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), - ipc: message.into(), - metadata: None, - }, - None, - None, - ); + let node_id = if node_id == "our" { our_name } else { node_id }; + Request::new() + .target(Address::new(node_id, "net:sys:uqbar").unwrap())? + .ipc(&message, serialize_message)? + .expects_response(5) + .send()?; + Ok(()) } "!message" => { - let (target_node, tail) = match tail.split_once(" ") { + let (node_id, tail) = match tail.split_once(" ") { Some((s, t)) => (s, t), - None => { - print_to_terminal(0, &format!("invalid command: \"{}\"", line)); - return; - } + None => return Err(anyhow!("invalid command: \"{line}\"")), }; let (target_process, ipc) = match tail.split_once(" ") { Some((a, p)) => (a, p), - None => { - print_to_terminal(0, &format!("invalid command: \"{}\"", line)); - return; - } + None => return Err(anyhow!("invalid command: \"{line}\"")), }; - // TODO: why does this work but using the API below does not? - // Is it related to passing json in rather than a Serialize type? - // - send_request( - &Address { - node: if target_node == "our" { - our_name.into() - } else { - target_node.into() - }, - process: ProcessId::from_str(target_process).unwrap_or_else(|_| { - ProcessId::from_str(&format!("{}:sys:uqbar", target_process)).unwrap() - }), - }, - &Request { - inherit: false, - expects_response: None, - ipc: ipc.into(), - metadata: None, - }, - None, - None, - ); - } - _ => { - print_to_terminal(0, &format!("invalid command: \"{line}\"")); + let node_id = if node_id == "our" { our_name } else { node_id }; + let process = ProcessId::from_str(target_process).unwrap_or_else(|_| { + ProcessId::from_str(&format!("{}:sys:uqbar", target_process)).unwrap() + }); + Request::new() + .target(Address::new(node_id, process).unwrap())? + .ipc(&ipc, serialize_message)? + .send()?; + Ok(()) } + _ => return Err(anyhow!("invalid command: \"{line}\"")), } } +struct Component; impl Guest for Component { fn init(our: String) { let our = Address::from_str(&our).unwrap(); - assert_eq!(our.process.to_string(), "terminal:terminal:uqbar"); - print_to_terminal(1, &format!("terminal: start")); + println!("terminal: start"); loop { - let (source, message) = match receive() { + let (source, message) = match wit::receive() { Ok((source, message)) => (source, message), Err((error, _context)) => { - print_to_terminal(0, &format!("net error: {:?}!", error.kind)); + println!("terminal: net error: {:?}!", error.kind); continue; } }; match message { - Message::Request(Request { + wit::Message::Request(wit::Request { expects_response, ipc, .. @@ -108,11 +76,14 @@ impl Guest for Component { if our.node != source.node || our.process != source.process { continue; } - parse_command(&our.node, std::str::from_utf8(&ipc).unwrap_or_default()); + match parse_command(&our.node, std::str::from_utf8(&ipc).unwrap_or_default()) { + Ok(()) => continue, + Err(e) => println!("terminal: {e}"), + } } - Message::Response((Response { ipc, metadata, .. }, _)) => { + wit::Message::Response((wit::Response { ipc, metadata, .. }, _)) => { if let Ok(txt) = std::str::from_utf8(&ipc) { - print_to_terminal(0, &format!("net response: {}", txt)); + println!("terminal: net response: {txt}"); } } } diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index d39d846b..b552727b 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -1,5 +1,5 @@ -#![allow(dead_code)] -use crate::uqbar::process::standard::*; +use crate::uqbar::process::standard as wit; +pub use crate::uqbar::process::standard::*; /// Uqbar process standard library for Rust compiled to WASM /// Must be used in context of bindings generated by uqbar.wit use serde::{Deserialize, Serialize}; @@ -12,15 +12,15 @@ wit_bindgen::generate!({ pub mod kernel_types; /// Override the println! macro to print to the terminal -/// TODO make this work -// macro_rules! println { -// () => { -// $print_to_terminal(0, "\n"); -// }; -// ($($arg:tt)*) => { -// $print_to_terminal(0, &format!($($arg)*)); -// }; -// } +#[macro_export] +macro_rules! println { + () => { + $crate::print_to_terminal(0, "\n"); + }; + ($($arg:tt)*) => {{ + $crate::print_to_terminal(0, &format!($($arg)*)); + }}; +} /// PackageId is like a ProcessId, but for a package. Only contains the name /// of the package and the name of the publisher. @@ -121,6 +121,22 @@ impl ProcessId { } } +pub trait IntoProcessId { + fn into_process_id(self) -> Result; +} + +impl IntoProcessId for ProcessId { + fn into_process_id(self) -> Result { + Ok(self) + } +} + +impl IntoProcessId for &str { + fn into_process_id(self) -> Result { + ProcessId::from_str(self) + } +} + impl std::fmt::Display for ProcessId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -181,6 +197,12 @@ impl std::error::Error for ProcessIdParseError { /// Address is defined in the wit bindings, but constructors and methods here. impl Address { + pub fn new(node: &str, process: T) -> Result { + Ok(Address { + node: node.to_string(), + process: process.into_process_id()?, + }) + } pub fn from_str(input: &str) -> Result { // split string on colons into 4 segments, // first one with @, next 3 with : @@ -222,6 +244,22 @@ impl Address { } } +pub trait IntoAddress { + fn into_address(self) -> Result; +} + +impl IntoAddress for Address { + fn into_address(self) -> Result { + Ok(self) + } +} + +impl IntoAddress for &str { + fn into_address(self) -> Result { + Address::from_str(self) + } +} + #[derive(Debug)] pub enum AddressParseError { TooManyColons, @@ -229,6 +267,30 @@ pub enum AddressParseError { MissingField, } +impl std::fmt::Display for AddressParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + AddressParseError::TooManyColons => "Too many colons in ProcessId string", + AddressParseError::MissingNodeId => "Node ID missing", + AddressParseError::MissingField => "Missing field in ProcessId string", + } + ) + } +} + +impl std::error::Error for AddressParseError { + fn description(&self) -> &str { + match self { + AddressParseError::TooManyColons => "Too many colons in ProcessId string", + AddressParseError::MissingNodeId => "Node ID missing", + AddressParseError::MissingField => "Missing field in ProcessId string", + } + } +} + /// /// Here, we define wrappers over the wit bindings to make them easier to use. /// This library prescribes the use of IPC and metadata types serialized and @@ -237,100 +299,278 @@ pub enum AddressParseError { /// For payloads, we use bincode to serialize and deserialize to bytes. /// -pub fn send_typed_request( - target: &Address, - inherit_payload_and_context: bool, - ipc: &T1, - metadata: Option, - payload: Option<&Payload>, +pub struct Request { + target: Option
, + inherit: bool, timeout: Option, -) -> anyhow::Result<()> -where - T1: serde::Serialize, -{ - crate::send_request( - target, - &Request { - inherit: inherit_payload_and_context, - expects_response: timeout, - ipc: serde_json::to_vec(ipc)?, - metadata, - }, - None, - payload, - ); - Ok(()) -} - -pub fn send_typed_response( - inherit_payload: bool, - ipc: &T1, + ipc: Option>, metadata: Option, - payload: Option<&Payload>, // will overwrite inherit flag if both are set -) -> anyhow::Result<()> -where - T1: serde::Serialize, -{ - crate::send_response( - &Response { - inherit: inherit_payload, - ipc: serde_json::to_vec(ipc)?, - metadata, - }, - payload, - ); - Ok(()) + payload: Option, + context: Option>, } -pub fn send_and_await_typed_response( - target: &Address, - inherit_payload_and_context: bool, - ipc: &T1, +impl Request { + pub fn new() -> Self { + Request { + target: None, + inherit: false, + timeout: None, + ipc: None, + metadata: None, + payload: None, + context: None, + } + } + + pub fn target(mut self, target: T) -> Result { + self.target = Some(target.into_address()?); + Ok(self) + } + + pub fn inherit(mut self, inherit: bool) -> Self { + self.inherit = inherit; + self + } + + pub fn expects_response(mut self, timeout: u64) -> Self { + self.timeout = Some(timeout); + self + } + + pub fn ipc_bytes(mut self, ipc: Vec) -> Self { + self.ipc = Some(ipc); + self + } + + pub fn ipc(mut self, ipc: &T, serializer: F) -> anyhow::Result + where + F: Fn(&T) -> anyhow::Result>, + { + self.ipc = Some(serializer(ipc)?); + Ok(self) + } + + pub fn metadata(mut self, metadata: Option) -> Self { + self.metadata = metadata; + self + } + + pub fn payload(mut self, payload: Option) -> Self { + self.payload = payload; + self + } + + pub fn payload_mime(mut self, mime: String) -> Self { + if self.payload.is_none() { + self.payload = Some(Payload { + mime: Some(mime), + bytes: vec![], + }); + self + } else { + self.payload = Some(Payload { + mime: Some(mime), + bytes: self.payload.unwrap().bytes, + }); + self + } + } + + pub fn payload_bytes(mut self, bytes: Vec) -> Self { + if self.payload.is_none() { + self.payload = Some(Payload { mime: None, bytes }); + self + } else { + self.payload = Some(Payload { + mime: self.payload.unwrap().mime, + bytes, + }); + self + } + } + + pub fn context_bytes(mut self, context: Option>) -> Self { + self.context = context; + self + } + + pub fn context(mut self, context: &T, serializer: F) -> anyhow::Result + where + F: Fn(&T) -> anyhow::Result>, + { + self.context = Some(serializer(context)?); + Ok(self) + } + + pub fn send(self) -> anyhow::Result<()> { + if let (Some(target), Some(ipc)) = (self.target, self.ipc) { + crate::send_request( + &target, + &wit::Request { + inherit: self.inherit, + expects_response: self.timeout, + ipc: serde_json::to_vec(&ipc)?, + metadata: self.metadata, + }, + self.context.as_ref(), + self.payload.as_ref(), + ); + Ok(()) + } else { + Err(anyhow::anyhow!("missing fields")) + } + } + + pub fn send_and_await_response(self) -> anyhow::Result> { + if let (Some(target), Some(ipc)) = (self.target, self.ipc) { + Ok(crate::send_and_await_response( + &target, + &wit::Request { + inherit: self.inherit, + expects_response: self.timeout, + ipc: serde_json::to_vec(&ipc)?, + metadata: self.metadata, + }, + self.payload.as_ref(), + )) + } else { + Err(anyhow::anyhow!("missing fields")) + } + } +} + +pub struct Response { + inherit: bool, + ipc: Option>, metadata: Option, - payload: Option<&Payload>, - timeout: u64, -) -> anyhow::Result> -where - T1: serde::Serialize, -{ - let res = crate::send_and_await_response( - target, - &Request { - inherit: inherit_payload_and_context, - expects_response: Some(timeout), - ipc: serde_json::to_vec(ipc)?, - metadata, - }, - payload, - ); - Ok(res) + payload: Option, } -pub fn get_typed_payload() -> Option { +impl Response { + pub fn new() -> Self { + Response { + inherit: false, + ipc: None, + metadata: None, + payload: None, + } + } + + pub fn inherit(mut self, inherit: bool) -> Self { + self.inherit = inherit; + self + } + + pub fn ipc_bytes(mut self, ipc: Vec) -> Self { + self.ipc = Some(ipc); + self + } + + pub fn ipc(mut self, ipc: &T, serializer: F) -> anyhow::Result + where + F: Fn(&T) -> anyhow::Result>, + { + self.ipc = Some(serializer(ipc)?); + Ok(self) + } + + pub fn metadata(mut self, metadata: Option) -> Self { + self.metadata = metadata; + self + } + + pub fn payload(mut self, payload: Option) -> Self { + self.payload = payload; + self + } + + pub fn payload_mime(mut self, mime: String) -> Self { + if self.payload.is_none() { + self.payload = Some(Payload { + mime: Some(mime), + bytes: vec![], + }); + self + } else { + self.payload = Some(Payload { + mime: Some(mime), + bytes: self.payload.unwrap().bytes, + }); + self + } + } + + pub fn payload_bytes(mut self, bytes: Vec) -> Self { + if self.payload.is_none() { + self.payload = Some(Payload { mime: None, bytes }); + self + } else { + self.payload = Some(Payload { + mime: self.payload.unwrap().mime, + bytes, + }); + self + } + } + + pub fn send(self) -> anyhow::Result<()> { + if let Some(ipc) = self.ipc { + crate::send_response( + &wit::Response { + inherit: self.inherit, + ipc: serde_json::to_vec(&ipc)?, + metadata: self.metadata, + }, + self.payload.as_ref(), + ); + Ok(()) + } else { + Err(anyhow::anyhow!("missing IPC")) + } + } +} + +pub fn make_payload(payload: &T, serializer: F) -> anyhow::Result +where + F: Fn(&T) -> anyhow::Result>, +{ + Ok(Payload { + mime: None, + bytes: serializer(payload)?, + }) +} + +pub fn get_typed_payload(deserializer: F) -> Option +where + F: Fn(&[u8]) -> anyhow::Result, +{ match crate::get_payload() { - Some(payload) => match bincode::deserialize::(&payload.bytes) { - Ok(bytes) => Some(bytes), + Some(payload) => match deserializer(&payload.bytes) { + Ok(thing) => Some(thing), Err(_) => None, }, None => None, } } -pub fn get_typed_state() -> Option { - match crate::get_state() { - Some(bytes) => match bincode::deserialize::(&bytes) { - Ok(state) => Some(state), - Err(_) => None, - }, - None => None, - } -} - -pub fn set_typed_state(state: &T) -> anyhow::Result<()> +pub fn get_typed_state(deserializer: F) -> Option where - T: serde::Serialize, + F: Fn(&[u8]) -> anyhow::Result, { - crate::set_state(&bincode::serialize(state)?); + match crate::get_state() { + Some(bytes) => match deserializer(&bytes) { + Ok(thing) => Some(thing), + Err(_) => None, + }, + None => None, + } +} + +pub fn set_typed_state(state: &T, serializer: F) -> anyhow::Result<()> +where + F: Fn(&T) -> anyhow::Result>, +{ + crate::set_state(&serializer(state)?); Ok(()) } diff --git a/src/process_lib.rs b/src/process_lib.rs deleted file mode 100644 index 44fa8c6f..00000000 --- a/src/process_lib.rs +++ /dev/null @@ -1,307 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::bindings::component::uq_process::types::*; -use super::bindings::{get_capability, share_capability, Address, Payload, ProcessId, SendError}; - -#[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] -pub struct PackageId { - pub package_name: String, - pub publisher_node: String, -} - -impl PackageId { - pub fn new(package_name: &str, publisher_node: &str) -> Self { - PackageId { - package_name: package_name.into(), - publisher_node: publisher_node.into(), - } - } - pub fn from_str(input: &str) -> Result { - // split string on colons into 2 segments - let mut segments = input.split(':'); - let package_name = segments - .next() - .ok_or(ProcessIdParseError::MissingField)? - .to_string(); - let publisher_node = segments - .next() - .ok_or(ProcessIdParseError::MissingField)? - .to_string(); - if segments.next().is_some() { - return Err(ProcessIdParseError::TooManyColons); - } - Ok(PackageId { - package_name, - publisher_node, - }) - } - pub fn to_string(&self) -> String { - [self.package_name.as_str(), self.publisher_node.as_str()].join(":") - } - pub fn package(&self) -> &str { - &self.package_name - } - pub fn publisher_node(&self) -> &str { - &self.publisher_node - } -} - -#[allow(dead_code)] -impl ProcessId { - /// generates a random u64 number if process_name is not declared - pub fn new(process_name: &str, package_name: &str, publisher_node: &str) -> Self { - ProcessId { - process_name: process_name.into(), - package_name: package_name.into(), - publisher_node: publisher_node.into(), - } - } - pub fn from_str(input: &str) -> Result { - // split string on colons into 3 segments - let mut segments = input.split(':'); - let process_name = segments - .next() - .ok_or(ProcessIdParseError::MissingField)? - .to_string(); - let package_name = segments - .next() - .ok_or(ProcessIdParseError::MissingField)? - .to_string(); - let publisher_node = segments - .next() - .ok_or(ProcessIdParseError::MissingField)? - .to_string(); - if segments.next().is_some() { - return Err(ProcessIdParseError::TooManyColons); - } - Ok(ProcessId { - process_name, - package_name, - publisher_node, - }) - } - pub fn to_string(&self) -> String { - [ - self.process_name.as_str(), - self.package_name.as_str(), - self.publisher_node.as_str(), - ] - .join(":") - } - pub fn process(&self) -> &str { - &self.process_name - } - pub fn package(&self) -> &str { - &self.package_name - } - pub fn publisher_node(&self) -> &str { - &self.publisher_node - } -} - -impl std::fmt::Display for ProcessId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}:{}:{}", - self.process_name, self.package_name, self.publisher_node - ) - } -} - -impl PartialEq for ProcessId { - fn eq(&self, other: &Self) -> bool { - self.process_name == other.process_name - && self.package_name == other.package_name - && self.publisher_node == other.publisher_node - } -} - -impl PartialEq<&str> for ProcessId { - fn eq(&self, other: &&str) -> bool { - &self.to_string() == other - } -} - -impl PartialEq for &str { - fn eq(&self, other: &ProcessId) -> bool { - self == &other.to_string() - } -} - -#[derive(Debug)] -pub enum ProcessIdParseError { - TooManyColons, - MissingField, -} - -impl std::fmt::Display for ProcessIdParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - ProcessIdParseError::TooManyColons => "Too many colons in ProcessId string", - ProcessIdParseError::MissingField => "Missing field in ProcessId string", - } - ) - } -} - -impl std::error::Error for ProcessIdParseError { - fn description(&self) -> &str { - match self { - ProcessIdParseError::TooManyColons => "Too many colons in ProcessId string", - ProcessIdParseError::MissingField => "Missing field in ProcessId string", - } - } -} - -impl Address { - pub fn from_str(input: &str) -> Result { - // split string on colons into 4 segments, - // first one with @, next 3 with : - let mut name_rest = input.split('@'); - let node = name_rest - .next() - .ok_or(AddressParseError::MissingField)? - .to_string(); - let mut segments = name_rest - .next() - .ok_or(AddressParseError::MissingNodeId)? - .split(':'); - let process_name = segments - .next() - .ok_or(AddressParseError::MissingField)? - .to_string(); - let package_name = segments - .next() - .ok_or(AddressParseError::MissingField)? - .to_string(); - let publisher_node = segments - .next() - .ok_or(AddressParseError::MissingField)? - .to_string(); - if segments.next().is_some() { - return Err(AddressParseError::TooManyColons); - } - Ok(Address { - node, - process: ProcessId { - process_name, - package_name, - publisher_node, - }, - }) - } - pub fn to_string(&self) -> String { - [self.node.as_str(), &self.process.to_string()].join("@") - } -} - -#[derive(Debug)] -pub enum AddressParseError { - TooManyColons, - MissingNodeId, - MissingField, -} - -pub fn send_and_await_response( - target: &Address, - inherit: bool, - ipc: Vec, - metadata: Option, - payload: Option<&Payload>, - timeout: u64, -) -> Result<(Address, Message), SendError> { - super::bindings::send_and_await_response( - target, - &Request { - inherit, - expects_response: Some(timeout), - ipc, - metadata, - }, - payload, - ) -} - -pub fn send_request( - target: &Address, - inherit: bool, - ipc: Vec, - metadata: Option, - context: Option<&Vec>, - payload: Option<&Payload>, -) { - super::bindings::send_request( - target, - &Request { - inherit, - expects_response: None, - ipc, - metadata, - }, - context, - payload, - ) -} - -pub fn get_state() -> Option { - match super::bindings::get_state() { - Some(bytes) => match bincode::deserialize::(&bytes) { - Ok(state) => Some(state), - Err(_) => None, - }, - None => None, - } -} - -pub fn set_state(state: &T) -where - T: serde::Serialize, -{ - super::bindings::set_state(&bincode::serialize(state).unwrap()); -} - -pub fn parse_message_ipc(json_bytes: &[u8]) -> anyhow::Result -where - for<'a> T: serde::Deserialize<'a>, -{ - let parsed: T = serde_json::from_slice(json_bytes)?; - Ok(parsed) -} - -pub fn grant_messaging(our: &Address, grant_to: &Vec) { - let Some(our_messaging_cap) = get_capability( - our, - &"\"messaging\"".into() - ) else { - panic!("missing self-messaging cap!") - }; - for process in grant_to { - share_capability(&process, &our_messaging_cap); - } -} - -// move these to better place! -#[derive(Serialize, Deserialize, Debug)] -pub enum FsAction { - Write, - Replace(u128), - Append(Option), - Read(u128), - ReadChunk(ReadChunkRequest), - Delete(u128), - Length(u128), - // process state management - GetState, - SetState, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct ReadChunkRequest { - pub file_uuid: u128, - pub start: u64, - pub length: u64, -} From c1064e64f57ec8b2c7312cbb00d53b99ade39613 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 30 Oct 2023 17:01:15 -0400 Subject: [PATCH 09/40] solid WIP using noise protocol [LOGIN BUGGED] --- Cargo.lock | 229 +++++++++++++-- Cargo.toml | 1 + src/main.rs | 5 +- src/net2/mod.rs | 733 ++++++++++++++++++++++++++++++++++++++++++++++++ src/register.rs | 17 +- 5 files changed, 951 insertions(+), 34 deletions(-) create mode 100644 src/net2/mod.rs diff --git a/Cargo.lock b/Cargo.lock index dc9b26ab..711addd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + [[package]] name = "aead" version = "0.5.2" @@ -46,6 +55,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug", +] + [[package]] name = "aes" version = "0.8.3" @@ -53,21 +74,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash 0.4.4", + "subtle", +] + [[package]] name = "aes-gcm" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", + "aead 0.5.2", + "aes 0.8.3", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.0", "subtle", ] @@ -298,6 +333,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "blake3" version = "1.4.1" @@ -499,6 +543,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -506,20 +562,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead 0.4.3", + "chacha20 0.8.2", + "cipher 0.3.0", + "poly1305 0.7.2", + "zeroize", +] + [[package]] name = "chacha20poly1305" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", + "aead 0.5.2", + "chacha20 0.9.1", + "cipher 0.4.4", + "poly1305 0.8.0", "zeroize", ] @@ -538,6 +607,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "cipher" version = "0.4.4" @@ -901,13 +979,49 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher 0.3.0", +] + [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", ] [[package]] @@ -1277,8 +1391,8 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "aes", - "ctr", + "aes 0.8.3", + "ctr 0.9.2", "digest 0.10.7", "hex", "hmac 0.12.1", @@ -1632,6 +1746,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a481586acf778f1b1455424c343f71124b048ffa5f4fc3f8f6ae9dc432dcb3c7" + [[package]] name = "file-per-thread-logger" version = "0.2.0" @@ -1880,6 +2000,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval 0.5.3", +] + [[package]] name = "ghash" version = "0.5.0" @@ -1887,7 +2017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug", - "polyval", + "polyval 0.6.1", ] [[package]] @@ -3208,6 +3338,23 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" + +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash 0.4.1", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -3216,7 +3363,19 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash 0.4.1", ] [[package]] @@ -3228,7 +3387,7 @@ dependencies = [ "cfg-if", "cpufeatures", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", ] [[package]] @@ -3828,7 +3987,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -4176,6 +4335,23 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +[[package]] +name = "snow" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" +dependencies = [ + "aes-gcm 0.9.4", + "blake2", + "chacha20poly1305 0.9.1", + "curve25519-dalek", + "rand_core", + "ring", + "rustc_version", + "sha2 0.10.7", + "subtle", +] + [[package]] name = "socket2" version = "0.4.9" @@ -4832,6 +5008,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "universal-hash" version = "0.5.1" @@ -4852,7 +5038,7 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" name = "uqbar" version = "0.1.0" dependencies = [ - "aes-gcm", + "aes-gcm 0.10.2", "anyhow", "async-recursion", "async-trait", @@ -4861,7 +5047,7 @@ dependencies = [ "blake3", "bytes", "cap-std", - "chacha20poly1305", + "chacha20poly1305 0.10.1", "chrono", "cita_trie", "crossterm", @@ -4897,6 +5083,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sha2 0.10.7", + "snow", "thiserror", "tokio", "tokio-tungstenite 0.20.0", @@ -5955,7 +6142,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ - "aes", + "aes 0.8.3", "byteorder", "bzip2", "constant_time_eq 0.1.5", diff --git a/Cargo.toml b/Cargo.toml index 0e80f3e6..6f38ebbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" serde_urlencoded = "0.7" sha2 = "0.10" +snow = { version = "0.9.3", features = ["ring-resolver"] } thiserror = "1.0.43" tokio = { version = "1.28", features = ["fs", "macros", "rt-multi-thread", "sync"] } tokio-tungstenite = "*" diff --git a/src/main.rs b/src/main.rs index 0b38c532..c5685f89 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use crate::types::*; use anyhow::Result; use dotenv; -use ethers::prelude::namehash; use std::env; use std::sync::Arc; use tokio::sync::{mpsc, oneshot}; @@ -14,7 +13,7 @@ mod http_client; mod http_server; mod kernel; mod keygen; -mod net; +mod net2; mod register; mod terminal; mod types; @@ -255,7 +254,7 @@ async fn main() { vfs_message_sender, encryptor_sender, )); - tasks.spawn(net::networking( + tasks.spawn(net2::networking( our.clone(), our_ip.to_string(), networking_keypair_arc.clone(), diff --git a/src/net2/mod.rs b/src/net2/mod.rs new file mode 100644 index 00000000..d45ba782 --- /dev/null +++ b/src/net2/mod.rs @@ -0,0 +1,733 @@ +use crate::types::*; +use anyhow::{anyhow, Result}; +use futures::stream::{SplitSink, SplitStream}; +use futures::{SinkExt, StreamExt}; +use ring::signature::{self, Ed25519KeyPair}; +use serde::{Deserialize, Serialize}; +use snow::params::NoiseParams; +use std::{collections::HashMap, sync::Arc}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use tokio::task::JoinSet; +use tokio::time::timeout; +use tokio_tungstenite::{ + accept_async, connect_async, tungstenite, MaybeTlsStream, WebSocketStream, +}; + +lazy_static::lazy_static! { + static ref PARAMS: NoiseParams = "Noise_XX_25519_ChaChaPoly_BLAKE2s" + .parse() + .expect("net: couldn't build noise params?"); +} + +// only used in connection initialization, otherwise, nacks and Responses are only used for "timeouts" +const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(15); + +const MESSAGE_MAX_SIZE: u32 = 104_858_000; // 100 MB -- TODO analyze as desired, apps can always chunk data into many messages + +#[derive(Clone, Debug, Serialize, Deserialize)] +enum NetActions { + QnsUpdate(QnsUpdate), + QnsBatchUpdate(Vec), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct QnsUpdate { + pub name: String, // actual username / domain name + pub owner: String, + pub node: String, // hex namehash of node + pub public_key: String, + pub ip: String, + pub port: u16, + pub routers: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +struct HandshakePayload { + pub name: NodeId, + pub signature: Vec, + pub protocol_version: u8, +} + +struct OpenConnection { + pub noise: snow::TransportState, + pub buf: Vec, + pub write_stream: SplitSink>, tungstenite::Message>, + pub read_stream: SplitStream>>, +} + +type Peers = HashMap>; +type PKINames = HashMap; // TODO maybe U256 to String +type OnchainPKI = HashMap; + +struct Peer { + pub identity: Identity, + pub sender: UnboundedSender, +} + +/// Entry point from the main kernel task. Runs forever, spawns listener and sender tasks. +pub async fn networking( + our: Identity, + our_ip: String, + keypair: Arc, + kernel_message_tx: MessageSender, + network_error_tx: NetworkErrorSender, + print_tx: PrintSender, + self_message_tx: MessageSender, + message_rx: MessageReceiver, +) -> Result<()> { + println!("networking\r"); + println!("our identity: {:#?}\r", our); + let our = Arc::new(our); + // branch on whether we are a direct or indirect node + match &our.ws_routing { + None => { + // indirect node: run the indirect networking strategy + todo!("TODO implement indirect networking strategy") + } + Some((ip, port)) => { + // direct node: run the direct networking strategy + if &our_ip != ip { + return Err(anyhow!( + "net: fatal error: IP address mismatch: {} != {}, update your QNS identity", + our_ip, + ip + )); + } + let tcp = match TcpListener::bind(format!("0.0.0.0:{}", port)).await { + Ok(tcp) => tcp, + Err(_e) => { + return Err(anyhow!( + "net: fatal error: can't listen on port {}, update your QNS identity or free up that port", + port, + )); + } + }; + direct_networking( + our.clone(), + our_ip, + tcp, + keypair, + kernel_message_tx, + network_error_tx, + print_tx, + self_message_tx, + message_rx, + ) + .await + } + } +} + +async fn direct_networking( + our: Arc, + our_ip: String, + tcp: TcpListener, + keypair: Arc, + kernel_message_tx: MessageSender, + network_error_tx: NetworkErrorSender, + print_tx: PrintSender, + self_message_tx: MessageSender, + mut message_rx: MessageReceiver, +) -> Result<()> { + println!("direct_networking\r"); + let mut pki: OnchainPKI = HashMap::new(); + let mut peers: Peers = HashMap::new(); + // mapping from QNS namehash to username + let mut names: PKINames = HashMap::new(); + + let mut active_connections = JoinSet::<(String, Option)>::new(); + + // 1. receive messages from kernel and send out over our connections + // 2. receive incoming TCP connections + // 3. deal with active connections that die by removing the associated peer + loop { + tokio::select! { + Some(km) = message_rx.recv() => { + // got a message from kernel to send out over the network + let target = &km.target.node; + // if the message is for us, it's either a protocol-level "hello" message, + // or a debugging command issued from our terminal. handle it here: + if target == &our.name { + match handle_local_message( + &our, + km, + &peers, + &mut pki, + &mut names, + &kernel_message_tx, + &print_tx, + ) + .await { + Ok(()) => {}, + Err(e) => { + print_tx.send(Printout { + verbosity: 0, + content: format!("net: error handling local message: {}", e) + }).await?; + } + } + } + // if the message is for a peer we currently have a connection with, + // try to send it to them + else if let Some(peer) = peers.get_mut(target) { + peer.sender.send(km)?; + } + else if let Some(peer_id) = pki.get(target) { + // if the message is for a *direct* peer we don't have a connection with, + // try to establish a connection with them + if peer_id.ws_routing.is_some() { + match init_connection(&our, &our_ip, peer_id, &keypair).await { + Ok((peer_name, conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id.clone(), + sender: peer_tx, + }); + peers.insert(peer_name, peer.clone()); + peer.sender.send(km)?; + active_connections.spawn(maintain_connection( + peer, + conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Err(e) => { + println!("net: error initializing connection: {}", e); + error_offline(km, &network_error_tx).await?; + } + } + } + // if the message is for an *indirect* peer we don't have a connection with, + // do some routing and shit + else { + todo!() + } + } + // peer cannot be found in PKI, throw an offline error + else { + error_offline(km, &network_error_tx).await?; + } + } + Ok((stream, _socket_addr)) = tcp.accept() => { + // TODO we can perform some amount of validation here + // to prevent some amount of potential DDoS attacks. + // can also block based on socket_addr + match accept_async(MaybeTlsStream::Plain(stream)).await { + Ok(websocket) => { + let (peer_name, conn) = recv_connection(&our, &pki, &keypair, websocket).await?; + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: pki.get(&peer_name).ok_or(anyhow!("jej"))?.clone(), + sender: peer_tx, + }); + peers.insert(peer_name, peer.clone()); + println!("received incoming connection\r"); + active_connections.spawn(maintain_connection( + peer, + conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + // ignore connections we failed to accept...? + Err(_) => {} + } + } + Some(Ok((dead_peer, maybe_resend))) = active_connections.join_next() => { + peers.remove(&dead_peer); + match maybe_resend { + None => {}, + Some(km) => { + self_message_tx.send(km).await?; + } + } + } + } + } +} + +async fn maintain_connection( + peer: Arc, + mut conn: OpenConnection, + mut peer_rx: UnboundedReceiver, + kernel_message_tx: MessageSender, + // network_error_tx: NetworkErrorSender, + // print_tx: PrintSender, +) -> (String, Option) { + println!("maintain_connection\r"); + loop { + tokio::select! { + recv_result = recv_uqbar_message(&mut conn) => { + match recv_result { + Ok(km) => { + if km.source.node != peer.identity.name { + println!("net: got message with spoofed source\r"); + return (peer.identity.name.clone(), None) + } + kernel_message_tx.send(km).await.expect("net error: fatal: kernel died"); + } + Err(e) => { + println!("net: error receiving message: {}\r", e); + return (peer.identity.name.clone(), None) + } + } + }, + maybe_recv = peer_rx.recv() => { + match maybe_recv { + Some(km) => { + // TODO error handle + match send_uqbar_message(&km, &mut conn).await { + Ok(()) => continue, + Err(e) => { + println!("net: error sending message: {}\r", e); + return (peer.identity.name.clone(), Some(km)) + } + } + } + None => { + println!("net: peer disconnected\r"); + return (peer.identity.name.clone(), None) + } + } + }, + } + } +} + +async fn recv_connection( + our: &Identity, + pki: &OnchainPKI, + keypair: &Ed25519KeyPair, + websocket: WebSocketStream>, +) -> Result<(String, OpenConnection)> { + println!("recv_connection\r"); + let mut buf = vec![0u8; 65535]; + let (mut noise, our_static_key) = build_responder(); + let (mut write_stream, mut read_stream) = websocket.split(); + + // <- e + noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; + + // -> e, ee, s, es + send_uqbar_handshake( + &our, + keypair, + &our_static_key, + &mut noise, + &mut buf, + &mut write_stream, + ) + .await?; + + // <- s, se + let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; + + // now validate this handshake payload against the QNS PKI + validate_handshake( + &their_handshake, + noise + .get_remote_static() + .ok_or(anyhow!("noise error: missing remote pubkey"))?, + pki.get(&their_handshake.name) + .ok_or(anyhow!("unknown QNS name"))?, + )?; + + // Transition the state machine into transport mode now that the handshake is complete. + let noise = noise.into_transport_mode()?; + println!("handshake complete, noise session received\r"); + + Ok(( + their_handshake.name, + OpenConnection { + noise, + buf, + write_stream, + read_stream, + }, + )) +} + +async fn init_connection( + our: &Identity, + our_ip: &str, + peer_id: &Identity, + keypair: &Ed25519KeyPair, +) -> Result<(String, OpenConnection)> { + println!("init_connection\r"); + let mut buf = vec![0u8; 65535]; + let (mut noise, our_static_key) = build_initiator(); + + let Some((ref ip, ref port)) = peer_id.ws_routing else { + return Err(anyhow!("target has no routing information")) + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")) + }; + let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { + return Err(anyhow!("failed to connect to target")) + }; + let (mut write_stream, mut read_stream) = websocket.split(); + + // -> e + let len = noise.write_message(&[], &mut buf)?; + ws_send(&mut write_stream, &buf[..len]).await?; + + // <- e, ee, s, es + let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; + + // now validate this handshake payload against the QNS PKI + validate_handshake( + &their_handshake, + noise + .get_remote_static() + .ok_or(anyhow!("noise error: missing remote pubkey"))?, + peer_id, + )?; + + // -> s, se + send_uqbar_handshake( + &our, + keypair, + &our_static_key, + &mut noise, + &mut buf, + &mut write_stream, + ) + .await?; + + let noise = noise.into_transport_mode()?; + println!("handshake complete, noise session initiated\r"); + + Ok(( + their_handshake.name, + OpenConnection { + noise, + buf, + write_stream, + read_stream, + }, + )) +} + +fn validate_handshake( + handshake: &HandshakePayload, + their_static_key: &[u8], + their_id: &Identity, +) -> Result<()> { + println!("validate_handshake\r"); + if handshake.protocol_version != 1 { + return Err(anyhow!("handshake protocol version mismatch")); + } + // verify their signature of their static key + let their_networking_key = signature::UnparsedPublicKey::new( + &signature::ED25519, + hex::decode(&strip_0x(&their_id.networking_key))?, + ); + their_networking_key.verify(their_static_key, &handshake.signature)?; + Ok(()) +} + +async fn send_uqbar_message(km: &KernelMessage, conn: &mut OpenConnection) -> Result<()> { + let serialized = bincode::serialize(km)?; + if serialized.len() > MESSAGE_MAX_SIZE as usize { + return Err(anyhow!("uqbar message too large")); + } + + let len = (serialized.len() as u32).to_be_bytes(); + let with_length_prefix = [len.to_vec(), serialized].concat(); + + for payload in with_length_prefix.chunks(65519) { // 65535 - 16 (TAGLEN) + let len = conn.noise.write_message(payload, &mut conn.buf)?; + ws_send(&mut conn.write_stream, &conn.buf[..len]).await?; + } + Ok(()) +} + +async fn recv_uqbar_message(conn: &mut OpenConnection) -> Result { + let outer_len = conn + .noise + .read_message(&ws_recv(&mut conn.read_stream).await?, &mut conn.buf)?; + if outer_len < 4 { + return Err(anyhow!("uqbar message too small!")); + } + + let length_bytes = [conn.buf[0], conn.buf[1], conn.buf[2], conn.buf[3]]; + let msg_len = u32::from_be_bytes(length_bytes); + + let mut msg = Vec::with_capacity(msg_len as usize); + msg.extend_from_slice(&conn.buf[4..outer_len]); + + while msg.len() < msg_len as usize { + let len = conn + .noise + .read_message(&ws_recv(&mut conn.read_stream).await?, &mut conn.buf)?; + msg.extend_from_slice(&conn.buf[..len]); + } + + Ok(bincode::deserialize(&msg)?) +} + +async fn send_uqbar_handshake( + our: &Identity, + keypair: &Ed25519KeyPair, + noise_static_key: &[u8], + noise: &mut snow::HandshakeState, + buf: &mut Vec, + write_stream: &mut SplitSink>, tungstenite::Message>, +) -> Result<()> { + println!("send_uqbar_handshake\r"); + let our_hs = bincode::serialize(&HandshakePayload { + name: our.name.clone(), + signature: keypair.sign(noise_static_key).as_ref().to_vec(), + protocol_version: 1, + }) + .expect("failed to serialize handshake payload"); + + let len = noise.write_message(&our_hs, buf)?; + ws_send(write_stream, &buf[..len]).await?; + + Ok(()) +} + +async fn recv_uqbar_handshake( + noise: &mut snow::HandshakeState, + buf: &mut Vec, + read_stream: &mut SplitStream>>, +) -> Result { + println!("recv_uqbar_handshake\r"); + let len = noise.read_message(&ws_recv(read_stream).await?, buf)?; + + // from buffer, read a sequence of bytes that deserializes to the + // 1. QNS name of the sender + // 2. a signature by their published networking key that signs the + // static key they will be using for this handshake + // 3. the version number of the networking protocol (so we can upgrade it) + Ok(bincode::deserialize(&buf[..len])?) +} + +async fn ws_recv( + read_stream: &mut SplitStream>>, +) -> Result> { + let Some(Ok(tungstenite::Message::Binary(bin))) = read_stream.next().await else { + return Err(anyhow!("websocket closed")); + }; + Ok(bin) +} + +async fn ws_send( + write_stream: &mut SplitSink>, tungstenite::Message>, + msg: &[u8], +) -> Result<()> { + write_stream.send(tungstenite::Message::binary(msg)).await?; + Ok(()) +} + +fn build_responder() -> (snow::HandshakeState, Vec) { + let builder: snow::Builder<'_> = snow::Builder::new(PARAMS.clone()); + let keypair = builder + .generate_keypair() + .expect("net: couldn't generate keypair?"); + ( + builder + .local_private_key(&keypair.private) + .build_responder() + .expect("net: couldn't build responder?"), + keypair.public, + ) +} + +fn build_initiator() -> (snow::HandshakeState, Vec) { + let builder: snow::Builder<'_> = snow::Builder::new(PARAMS.clone()); + let keypair = builder + .generate_keypair() + .expect("net: couldn't generate keypair?"); + ( + builder + .local_private_key(&keypair.private) + .build_initiator() + .expect("net: couldn't build responder?"), + keypair.public, + ) +} + +fn make_ws_url(our_ip: &str, ip: &str, port: &u16) -> Result { + // if we have the same public IP as target, route locally, + // otherwise they will appear offline due to loopback stuff + let ip = if our_ip == ip { "localhost" } else { ip }; + match url::Url::parse(&format!("ws://{}:{}/ws", ip, port)) { + Ok(v) => Ok(v), + Err(_) => Err(SendErrorKind::Offline), + } +} + +async fn error_offline(km: KernelMessage, network_error_tx: &NetworkErrorSender) -> Result<()> { + network_error_tx + .send(WrappedSendError { + id: km.id, + source: km.source, + error: SendError { + kind: SendErrorKind::Offline, + target: km.target, + message: km.message, + payload: km.payload, + }, + }) + .await?; + Ok(()) +} + +fn strip_0x(s: &str) -> String { + if s.starts_with("0x") { + s[2..].to_string() + } else { + s.to_string() + } +} + +/// net module only handles incoming local requests, will never return a response +async fn handle_local_message( + our: &Identity, + km: KernelMessage, + peers: &Peers, + pki: &mut OnchainPKI, + names: &mut PKINames, + kernel_message_tx: &MessageSender, + print_tx: &PrintSender, +) -> Result<()> { + let ipc = match km.message { + Message::Response(_) => return Ok(()), + Message::Request(request) => request.ipc, + }; + + if km.source.node != our.name { + // respond to a text message with a simple "delivered" response + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "\x1b[3;32m{}: {}\x1b[0m", + km.source.node, + std::str::from_utf8(&ipc).unwrap_or("!!message parse error!!") + ), + }) + .await?; + kernel_message_tx + .send(KernelMessage { + id: km.id, + source: Address { + node: our.name.clone(), + process: ProcessId::from_str("net:sys:uqbar").unwrap(), + }, + target: km.rsvp.unwrap_or(km.source), + rsvp: None, + message: Message::Response(( + Response { + inherit: false, + ipc: "delivered".as_bytes().to_vec(), + metadata: None, + }, + None, + )), + payload: None, + signed_capabilities: None, + }) + .await?; + Ok(()) + } else { + // available commands: "peers", "QnsUpdate" (see qns_indexer module) + // first parse as raw string, then deserialize to NetActions object + match std::str::from_utf8(&ipc) { + Ok("peers") => { + print_tx + .send(Printout { + verbosity: 0, + content: format!("{:#?}", peers.keys()), + }) + .await?; + } + Ok("pki") => { + print_tx + .send(Printout { + verbosity: 0, + content: format!("{:#?}", pki), + }) + .await?; + } + Ok("names") => { + print_tx + .send(Printout { + verbosity: 0, + content: format!("{:#?}", names), + }) + .await?; + } + _ => { + let Ok(act) = serde_json::from_slice::(&ipc) else { + print_tx + .send(Printout { + verbosity: 0, + content: "net: got unknown command".into(), + }) + .await?; + return Ok(()); + }; + match act { + NetActions::QnsUpdate(log) => { + print_tx + .send(Printout { + verbosity: 1, + content: format!("net: got QNS update for {}", log.name), + }) + .await?; + + pki.insert( + log.name.clone(), + Identity { + name: log.name.clone(), + networking_key: log.public_key, + ws_routing: if log.ip == "0.0.0.0".to_string() || log.port == 0 { + None + } else { + Some((log.ip, log.port)) + }, + allowed_routers: log.routers, + }, + ); + names.insert(log.node, log.name); + } + NetActions::QnsBatchUpdate(log_list) => { + print_tx + .send(Printout { + verbosity: 1, + content: format!( + "net: got QNS update with {} peers", + log_list.len() + ), + }) + .await?; + for log in log_list { + pki.insert( + log.name.clone(), + Identity { + name: log.name.clone(), + networking_key: log.public_key, + ws_routing: if log.ip == "0.0.0.0".to_string() || log.port == 0 + { + None + } else { + Some((log.ip, log.port)) + }, + allowed_routers: log.routers, + }, + ); + names.insert(log.node, log.name); + } + } + } + } + } + Ok(()) + } +} diff --git a/src/register.rs b/src/register.rs index 71f7a655..ae57fc3d 100644 --- a/src/register.rs +++ b/src/register.rs @@ -159,11 +159,12 @@ async fn handle_boot( ) -> Result { our.name = info.username; - if info.direct { - our.allowed_routers = vec![]; - } else { - our.ws_routing = None; - } + // println!("handle_boot: info.direct: {}\r", info.direct); + // if info.direct { + // our.allowed_routers = vec![]; + // } else { + // our.ws_routing = None; + // } // if keyfile was not present in node and is present from user upload if encoded_keyfile.is_empty() && !info.keyfile.clone().is_empty() { @@ -260,11 +261,7 @@ async fn handle_info( networking_key: format!("0x{}", public_key), name: String::new(), ws_routing: Some((ip.clone(), ws_port)), - allowed_routers: vec![ - "uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(), - "uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(), - "uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(), - ], + allowed_routers: vec![], }; *our_arc.lock().unwrap() = Some(our.clone()); From f04b72582cf271eb798edf0f6337470bbc9cc3bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 21:01:35 +0000 Subject: [PATCH 10/40] Format Rust code using rustfmt --- src/net2/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/net2/mod.rs b/src/net2/mod.rs index d45ba782..6faf7523 100644 --- a/src/net2/mod.rs +++ b/src/net2/mod.rs @@ -360,13 +360,13 @@ async fn init_connection( let (mut noise, our_static_key) = build_initiator(); let Some((ref ip, ref port)) = peer_id.ws_routing else { - return Err(anyhow!("target has no routing information")) + return Err(anyhow!("target has no routing information")); }; let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")) + return Err(anyhow!("failed to parse websocket url")); }; let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { - return Err(anyhow!("failed to connect to target")) + return Err(anyhow!("failed to connect to target")); }; let (mut write_stream, mut read_stream) = websocket.split(); @@ -438,7 +438,8 @@ async fn send_uqbar_message(km: &KernelMessage, conn: &mut OpenConnection) -> Re let len = (serialized.len() as u32).to_be_bytes(); let with_length_prefix = [len.to_vec(), serialized].concat(); - for payload in with_length_prefix.chunks(65519) { // 65535 - 16 (TAGLEN) + for payload in with_length_prefix.chunks(65519) { + // 65535 - 16 (TAGLEN) let len = conn.noise.write_message(payload, &mut conn.buf)?; ws_send(&mut conn.write_stream, &conn.buf[..len]).await?; } From e25a610fd1b1279232f60c3fabc439576402cbd8 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 30 Oct 2023 22:00:20 -0400 Subject: [PATCH 11/40] WIP2 --- src/net2/mod.rs | 328 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 282 insertions(+), 46 deletions(-) diff --git a/src/net2/mod.rs b/src/net2/mod.rs index 6faf7523..c7d2deb2 100644 --- a/src/net2/mod.rs +++ b/src/net2/mod.rs @@ -2,6 +2,7 @@ use crate::types::*; use anyhow::{anyhow, Result}; use futures::stream::{SplitSink, SplitStream}; use futures::{SinkExt, StreamExt}; +use rand::seq::SliceRandom; use ring::signature::{self, Ed25519KeyPair}; use serde::{Deserialize, Serialize}; use snow::params::NoiseParams; @@ -45,17 +46,43 @@ struct QnsUpdate { #[derive(Debug, Deserialize, Serialize)] struct HandshakePayload { pub name: NodeId, + // signature is created by their networking key, of their static key + // someone could reuse this signature, but then they will be unable + // to encrypt messages to us. pub signature: Vec, pub protocol_version: u8, } -struct OpenConnection { +#[derive(Debug, Deserialize, Serialize)] +struct RoutingRequest { + pub name: NodeId, + // signature is created by their networking key, of the [target, router name].concat() + // someone could reuse this signature, and TODO need to find a way + // to make that useless in this routing request case. + pub signature: Vec, + pub target: NodeId, + pub protocol_version: u8, +} + +enum Connection { + Peer(PeerConnection), + Passthrough(PassthroughConnection), +} + +struct PeerConnection { pub noise: snow::TransportState, pub buf: Vec, pub write_stream: SplitSink>, tungstenite::Message>, pub read_stream: SplitStream>>, } +struct PassthroughConnection { + pub write_stream_1: SplitSink>, tungstenite::Message>, + pub read_stream_1: SplitStream>>, + pub write_stream_2: SplitSink>, tungstenite::Message>, + pub read_stream_2: SplitStream>>, +} + type Peers = HashMap>; type PKINames = HashMap; // TODO maybe U256 to String type OnchainPKI = HashMap; @@ -136,13 +163,12 @@ async fn direct_networking( // mapping from QNS namehash to username let mut names: PKINames = HashMap::new(); - let mut active_connections = JoinSet::<(String, Option)>::new(); + let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); + let mut forwarding_connections = JoinSet::::new(); - // 1. receive messages from kernel and send out over our connections - // 2. receive incoming TCP connections - // 3. deal with active connections that die by removing the associated peer loop { tokio::select! { + // 1. receive messages from kernel and send out over our connections Some(km) = message_rx.recv() => { // got a message from kernel to send out over the network let target = &km.target.node; @@ -177,8 +203,8 @@ async fn direct_networking( // if the message is for a *direct* peer we don't have a connection with, // try to establish a connection with them if peer_id.ws_routing.is_some() { - match init_connection(&our, &our_ip, peer_id, &keypair).await { - Ok((peer_name, conn)) => { + match init_connection(&our, &our_ip, peer_id, &keypair, None).await { + Ok((peer_name, direct_conn)) => { let (peer_tx, peer_rx) = unbounded_channel::(); let peer = Arc::new(Peer { identity: peer_id.clone(), @@ -186,23 +212,40 @@ async fn direct_networking( }); peers.insert(peer_name, peer.clone()); peer.sender.send(km)?; - active_connections.spawn(maintain_connection( + peer_connections.spawn(maintain_connection( peer, - conn, + direct_conn, peer_rx, kernel_message_tx.clone(), )); } Err(e) => { - println!("net: error initializing connection: {}", e); + println!("net: error initializing connection: {}\r", e); error_offline(km, &network_error_tx).await?; } } } // if the message is for an *indirect* peer we don't have a connection with, - // do some routing and shit + // do some routing: in a randomized order, go through their listed routers + // on chain and try to get one of them to build a proxied connection to + // this node for you else { - todo!() + let sent = init_connection_via_router( + &our, + &our_ip, + &keypair, + km.clone(), + peer_id, + &pki, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone() + ).await; + if !sent { + // none of the routers worked! + println!("net: error initializing routed connection\r"); + error_offline(km, &network_error_tx).await?; + } } } // peer cannot be found in PKI, throw an offline error @@ -210,32 +253,45 @@ async fn direct_networking( error_offline(km, &network_error_tx).await?; } } + // 2. receive incoming TCP connections Ok((stream, _socket_addr)) = tcp.accept() => { // TODO we can perform some amount of validation here // to prevent some amount of potential DDoS attacks. // can also block based on socket_addr match accept_async(MaybeTlsStream::Plain(stream)).await { Ok(websocket) => { - let (peer_name, conn) = recv_connection(&our, &pki, &keypair, websocket).await?; + let (peer_id, conn) = recv_connection(&our, &our_ip, &pki, &keypair, websocket).await?; let (peer_tx, peer_rx) = unbounded_channel::(); let peer = Arc::new(Peer { - identity: pki.get(&peer_name).ok_or(anyhow!("jej"))?.clone(), + identity: peer_id, sender: peer_tx, }); - peers.insert(peer_name, peer.clone()); - println!("received incoming connection\r"); - active_connections.spawn(maintain_connection( - peer, - conn, - peer_rx, - kernel_message_tx.clone(), - )); + // if conn is direct, add peer + // if passthrough, add to our forwarding connections joinset + match conn { + Connection::Peer(peer_conn) => { + peers.insert(peer.identity.name.clone(), peer.clone()); + peer_connections.spawn(maintain_connection( + peer, + peer_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Connection::Passthrough(passthrough_conn) => { + forwarding_connections.spawn(maintain_passthrough( + peer, + passthrough_conn, + )); + } + } } // ignore connections we failed to accept...? Err(_) => {} } } - Some(Ok((dead_peer, maybe_resend))) = active_connections.join_next() => { + // 3. deal with active connections that die by removing the associated peer + Some(Ok((dead_peer, maybe_resend))) = peer_connections.join_next() => { peers.remove(&dead_peer); match maybe_resend { None => {}, @@ -248,14 +304,58 @@ async fn direct_networking( } } +async fn init_connection_via_router( + our: &Identity, + our_ip: &str, + keypair: &Ed25519KeyPair, + km: KernelMessage, + peer_id: &Identity, + pki: &OnchainPKI, + peers: &mut Peers, + peer_connections: &mut JoinSet<(NodeId, Option)>, + kernel_message_tx: MessageSender, +) -> bool { + let routers_shuffled = { + let mut routers = peer_id.allowed_routers.clone(); + routers.shuffle(&mut rand::thread_rng()); + routers + }; + for router in routers_shuffled { + let router_id = match pki.get(&router) { + None => continue, + Some(id) => id, + }; + match init_connection(&our, &our_ip, peer_id, &keypair, Some(router_id)).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id.clone(), + sender: peer_tx, + }); + peers.insert(peer_name, peer.clone()); + peer.sender.send(km).unwrap(); + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + )); + return true; + } + Err(_) => continue, + } + } + return false; +} + async fn maintain_connection( peer: Arc, - mut conn: OpenConnection, + mut conn: PeerConnection, mut peer_rx: UnboundedReceiver, kernel_message_tx: MessageSender, // network_error_tx: NetworkErrorSender, // print_tx: PrintSender, -) -> (String, Option) { +) -> (NodeId, Option) { println!("maintain_connection\r"); loop { tokio::select! { @@ -296,19 +396,69 @@ async fn maintain_connection( } } +/// match the streams +/// TODO optimize performance of this +async fn maintain_passthrough(peer: Arc, mut conn: PassthroughConnection) -> NodeId { + println!("maintain_passthrough\r"); + loop { + tokio::select! { + maybe_recv = conn.read_stream_1.next() => { + match maybe_recv { + Some(Ok(msg)) => { + conn.write_stream_2.send(msg).await.expect("net error: fatal: kernel died"); + } + _ => { + println!("net: passthrough broke\r"); + return peer.identity.name.clone() + } + } + }, + maybe_recv = conn.read_stream_2.next() => { + match maybe_recv { + Some(Ok(msg)) => { + conn.write_stream_1.send(msg).await.expect("net error: fatal: kernel died"); + } + _ => { + println!("net: passthrough broke\r"); + return peer.identity.name.clone() + } + } + }, + } + } +} + async fn recv_connection( our: &Identity, + our_ip: &str, pki: &OnchainPKI, keypair: &Ed25519KeyPair, websocket: WebSocketStream>, -) -> Result<(String, OpenConnection)> { +) -> Result<(Identity, Connection)> { println!("recv_connection\r"); let mut buf = vec![0u8; 65535]; let (mut noise, our_static_key) = build_responder(); let (mut write_stream, mut read_stream) = websocket.split(); // <- e - noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; + let len = noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; + + // if the first message contains a "routing request", + // we evaluate whether we want to perform routing for them + if len != 0 { + let (their_id, target_name) = validate_routing_request(&our.name, &buf, pki)?; + // TODO evaluate whether we want to perform routing for them! + // if we do, produce this thing: + return create_passthrough( + our_ip, + their_id, + target_name, + pki, + write_stream, + read_stream, + ) + .await; + } // -> e, ee, s, es send_uqbar_handshake( @@ -325,13 +475,15 @@ async fn recv_connection( let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; // now validate this handshake payload against the QNS PKI + let their_id = pki + .get(&their_handshake.name) + .ok_or(anyhow!("unknown QNS name"))?; validate_handshake( &their_handshake, noise .get_remote_static() .ok_or(anyhow!("noise error: missing remote pubkey"))?, - pki.get(&their_handshake.name) - .ok_or(anyhow!("unknown QNS name"))?, + their_id, )?; // Transition the state machine into transport mode now that the handshake is complete. @@ -339,13 +491,13 @@ async fn recv_connection( println!("handshake complete, noise session received\r"); Ok(( - their_handshake.name, - OpenConnection { + their_id.clone(), + Connection::Peer(PeerConnection { noise, buf, write_stream, read_stream, - }, + }), )) } @@ -354,24 +506,56 @@ async fn init_connection( our_ip: &str, peer_id: &Identity, keypair: &Ed25519KeyPair, -) -> Result<(String, OpenConnection)> { + use_router: Option<&Identity>, +) -> Result<(String, PeerConnection)> { println!("init_connection\r"); let mut buf = vec![0u8; 65535]; let (mut noise, our_static_key) = build_initiator(); - let Some((ref ip, ref port)) = peer_id.ws_routing else { - return Err(anyhow!("target has no routing information")); + let (mut write_stream, mut read_stream) = match use_router { + None => { + let Some((ref ip, ref port)) = peer_id.ws_routing else { + return Err(anyhow!("target has no routing information")); + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { + return Err(anyhow!("failed to connect to target")); + }; + websocket.split() + } + Some(router_id) => { + let Some((ref ip, ref port)) = router_id.ws_routing else { + return Err(anyhow!("router has no routing information")); + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { + return Err(anyhow!("failed to connect to target")); + }; + websocket.split() + } }; - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { - return Err(anyhow!("failed to connect to target")); - }; - let (mut write_stream, mut read_stream) = websocket.split(); // -> e - let len = noise.write_message(&[], &mut buf)?; + let message = match use_router { + None => vec![], + Some(router_id) => { + let routing_request = RoutingRequest { + name: our.name.clone(), + signature: keypair + .sign([&peer_id.name, router_id.name.as_str()].concat().as_bytes()) + .as_ref() + .to_vec(), + target: peer_id.name.clone(), + protocol_version: 1, + }; + bincode::serialize(&routing_request)? + } + }; + let len = noise.write_message(&message, &mut buf)?; ws_send(&mut write_stream, &buf[..len]).await?; // <- e, ee, s, es @@ -402,7 +586,7 @@ async fn init_connection( Ok(( their_handshake.name, - OpenConnection { + PeerConnection { noise, buf, write_stream, @@ -411,6 +595,58 @@ async fn init_connection( )) } +async fn create_passthrough( + our_ip: &str, + from_id: Identity, + to_name: NodeId, + pki: &OnchainPKI, + write_stream_1: SplitSink>, tungstenite::Message>, + read_stream_1: SplitStream>>, +) -> Result<(Identity, Connection)> { + let to_id = pki.get(&to_name).ok_or(anyhow!("unknown QNS name"))?; + let Some((ref ip, ref port)) = to_id.ws_routing else { + return Err(anyhow!("target has no routing information")); + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { + return Err(anyhow!("failed to connect to target")); + }; + let (write_stream_2, read_stream_2) = websocket.split(); + + Ok(( + from_id, + Connection::Passthrough(PassthroughConnection { + write_stream_1, + read_stream_1, + write_stream_2, + read_stream_2, + }), + )) +} + +fn validate_routing_request( + our_name: &str, + buf: &[u8], + pki: &OnchainPKI, +) -> Result<(Identity, NodeId)> { + println!("validate_routing_request\r"); + let routing_request: RoutingRequest = bincode::deserialize(buf)?; + let their_id = pki + .get(&routing_request.name) + .ok_or(anyhow!("unknown QNS name"))?; + let their_networking_key = signature::UnparsedPublicKey::new( + &signature::ED25519, + hex::decode(&strip_0x(&their_id.networking_key))?, + ); + their_networking_key.verify( + &[&routing_request.target, our_name].concat().as_bytes(), + &routing_request.signature, + )?; + Ok((their_id.clone(), routing_request.target)) +} + fn validate_handshake( handshake: &HandshakePayload, their_static_key: &[u8], @@ -429,7 +665,7 @@ fn validate_handshake( Ok(()) } -async fn send_uqbar_message(km: &KernelMessage, conn: &mut OpenConnection) -> Result<()> { +async fn send_uqbar_message(km: &KernelMessage, conn: &mut PeerConnection) -> Result<()> { let serialized = bincode::serialize(km)?; if serialized.len() > MESSAGE_MAX_SIZE as usize { return Err(anyhow!("uqbar message too large")); @@ -446,7 +682,7 @@ async fn send_uqbar_message(km: &KernelMessage, conn: &mut OpenConnection) -> Re Ok(()) } -async fn recv_uqbar_message(conn: &mut OpenConnection) -> Result { +async fn recv_uqbar_message(conn: &mut PeerConnection) -> Result { let outer_len = conn .noise .read_message(&ws_recv(&mut conn.read_stream).await?, &mut conn.buf)?; From ba53609cb58b196f01611cbe4be8efc80cdc2b48 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 02:00:40 +0000 Subject: [PATCH 12/40] Format Rust code using rustfmt --- src/net2/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/net2/mod.rs b/src/net2/mod.rs index c7d2deb2..66ee9af4 100644 --- a/src/net2/mod.rs +++ b/src/net2/mod.rs @@ -520,7 +520,8 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { + let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await + else { return Err(anyhow!("failed to connect to target")); }; websocket.split() @@ -532,7 +533,8 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { + let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await + else { return Err(anyhow!("failed to connect to target")); }; websocket.split() From 04c65fe1198122d4819cbc21d67806d3ac6e75fd Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Tue, 31 Oct 2023 15:41:25 -0400 Subject: [PATCH 13/40] WIP working direct and indirect, need to test and parallelize sends to different targets --- src/net2/mod.rs | 967 ++++++++++++++++++++++++++++------------------ src/net2/types.rs | 115 ++++++ src/net2/utils.rs | 301 +++++++++++++++ src/register | 2 +- src/register.rs | 21 +- 5 files changed, 1019 insertions(+), 387 deletions(-) create mode 100644 src/net2/types.rs create mode 100644 src/net2/utils.rs diff --git a/src/net2/mod.rs b/src/net2/mod.rs index 66ee9af4..97e668df 100644 --- a/src/net2/mod.rs +++ b/src/net2/mod.rs @@ -1,96 +1,27 @@ +use crate::net2::{types::*, utils::*}; use crate::types::*; use anyhow::{anyhow, Result}; -use futures::stream::{SplitSink, SplitStream}; use futures::{SinkExt, StreamExt}; use rand::seq::SliceRandom; -use ring::signature::{self, Ed25519KeyPair}; -use serde::{Deserialize, Serialize}; -use snow::params::NoiseParams; -use std::{collections::HashMap, sync::Arc}; -use tokio::net::{TcpListener, TcpStream}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use tokio::task::JoinSet; -use tokio::time::timeout; -use tokio_tungstenite::{ - accept_async, connect_async, tungstenite, MaybeTlsStream, WebSocketStream, +use ring::signature::Ed25519KeyPair; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, }; +use tokio::net::TcpListener; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; +use tokio::task::JoinSet; +use tokio::time; +use tokio_tungstenite::{accept_async, connect_async, MaybeTlsStream, WebSocketStream}; -lazy_static::lazy_static! { - static ref PARAMS: NoiseParams = "Noise_XX_25519_ChaChaPoly_BLAKE2s" - .parse() - .expect("net: couldn't build noise params?"); -} +mod types; +mod utils; // only used in connection initialization, otherwise, nacks and Responses are only used for "timeouts" -const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(15); +const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); -const MESSAGE_MAX_SIZE: u32 = 104_858_000; // 100 MB -- TODO analyze as desired, apps can always chunk data into many messages - -#[derive(Clone, Debug, Serialize, Deserialize)] -enum NetActions { - QnsUpdate(QnsUpdate), - QnsBatchUpdate(Vec), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -struct QnsUpdate { - pub name: String, // actual username / domain name - pub owner: String, - pub node: String, // hex namehash of node - pub public_key: String, - pub ip: String, - pub port: u16, - pub routers: Vec, -} - -#[derive(Debug, Deserialize, Serialize)] -struct HandshakePayload { - pub name: NodeId, - // signature is created by their networking key, of their static key - // someone could reuse this signature, but then they will be unable - // to encrypt messages to us. - pub signature: Vec, - pub protocol_version: u8, -} - -#[derive(Debug, Deserialize, Serialize)] -struct RoutingRequest { - pub name: NodeId, - // signature is created by their networking key, of the [target, router name].concat() - // someone could reuse this signature, and TODO need to find a way - // to make that useless in this routing request case. - pub signature: Vec, - pub target: NodeId, - pub protocol_version: u8, -} - -enum Connection { - Peer(PeerConnection), - Passthrough(PassthroughConnection), -} - -struct PeerConnection { - pub noise: snow::TransportState, - pub buf: Vec, - pub write_stream: SplitSink>, tungstenite::Message>, - pub read_stream: SplitStream>>, -} - -struct PassthroughConnection { - pub write_stream_1: SplitSink>, tungstenite::Message>, - pub read_stream_1: SplitStream>>, - pub write_stream_2: SplitSink>, tungstenite::Message>, - pub read_stream_2: SplitStream>>, -} - -type Peers = HashMap>; -type PKINames = HashMap; // TODO maybe U256 to String -type OnchainPKI = HashMap; - -struct Peer { - pub identity: Identity, - pub sender: UnboundedSender, -} +// 10 MB -- TODO analyze as desired, apps can always chunk data into many messages +const MESSAGE_MAX_SIZE: u32 = 10_485_800; /// Entry point from the main kernel task. Runs forever, spawns listener and sender tasks. pub async fn networking( @@ -103,14 +34,23 @@ pub async fn networking( self_message_tx: MessageSender, message_rx: MessageReceiver, ) -> Result<()> { - println!("networking\r"); + println!("networking!\r"); println!("our identity: {:#?}\r", our); - let our = Arc::new(our); // branch on whether we are a direct or indirect node match &our.ws_routing { None => { // indirect node: run the indirect networking strategy - todo!("TODO implement indirect networking strategy") + indirect_networking( + our, + our_ip, + keypair, + kernel_message_tx, + network_error_tx, + print_tx, + self_message_tx, + message_rx, + ) + .await } Some((ip, port)) => { // direct node: run the direct networking strategy @@ -131,7 +71,7 @@ pub async fn networking( } }; direct_networking( - our.clone(), + our, our_ip, tcp, keypair, @@ -146,8 +86,212 @@ pub async fn networking( } } +async fn indirect_networking( + our: Identity, + our_ip: String, + keypair: Arc, + kernel_message_tx: MessageSender, + network_error_tx: NetworkErrorSender, + print_tx: PrintSender, + self_message_tx: MessageSender, + mut message_rx: MessageReceiver, +) -> Result<()> { + println!("indirect_networking\r"); + let mut pki: OnchainPKI = HashMap::new(); + let mut peers: Peers = HashMap::new(); + // mapping from QNS namehash to username + let mut names: PKINames = HashMap::new(); + + let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); + let mut active_routers = HashSet::::new(); + + // before opening up the main loop, go through our allowed routers + // and attempt to connect to all of them, saving the successfully + // connected-to ones in our router-set + connect_to_routers( + &our, + &our_ip, + &keypair, + &mut active_routers, + &pki, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone(), + ) + .await; + + loop { + tokio::select! { + // 1. receive messages from kernel and send out over connections, + // making new connections through our router-set as needed + Some(km) = message_rx.recv() => { + // got a message from kernel to send out over the network + let target = &km.target.node; + // if the message is for us, it's either a protocol-level "hello" message, + // or a debugging command issued from our terminal. handle it here: + if target == &our.name { + match handle_local_message( + &our, + &our_ip, + &keypair, + km, + &mut peers, + &mut pki, + &mut peer_connections, + None, + None, + Some(&active_routers), + &mut names, + &kernel_message_tx, + &print_tx, + ) + .await { + Ok(()) => {}, + Err(e) => { + print_tx.send(Printout { + verbosity: 0, + content: format!("net: error handling local message: {}", e) + }).await?; + } + } + } + // if the message is for a peer we currently have a connection with, + // try to send it to them + else if let Some(peer) = peers.get_mut(target) { + peer.sender.send(km)?; + } + else if let Some(peer_id) = pki.get(target) { + // if the message is for a *direct* peer we don't have a connection with, + // try to establish a connection with them + // TODO: here, we can *choose* to use our routers so as not to reveal + // networking information about ourselves to the target. + if peer_id.ws_routing.is_some() { + match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id.clone(), + routing_for: false, + sender: peer_tx, + }); + peers.insert(peer_name, peer.clone()); + peer.sender.send(km)?; + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Err(e) => { + println!("net: error initializing connection: {}\r", e); + error_offline(km, &network_error_tx).await?; + } + } + } + // if the message is for an *indirect* peer we don't have a connection with, + // do some routing: in a randomized order, go through their listed routers + // on chain and try to get one of them to build a proxied connection to + // this node for you + else { + let sent = time::timeout(TIMEOUT, + init_connection_via_router( + &our, + &our_ip, + &keypair, + km.clone(), + peer_id, + &pki, + &names, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone() + )).await; + if !sent.unwrap_or(false) { + // none of the routers worked! + println!("net: error initializing routed connection\r"); + error_offline(km, &network_error_tx).await?; + } + } + } + // peer cannot be found in PKI, throw an offline error + else { + error_offline(km, &network_error_tx).await?; + } + } + // 2. deal with active connections that die by removing the associated peer + // if the peer is one of our routers, remove them from router-set + Some(Ok((dead_peer, maybe_resend))) = peer_connections.join_next() => { + peers.remove(&dead_peer); + active_routers.remove(&dead_peer); + match maybe_resend { + None => {}, + Some(km) => { + self_message_tx.send(km).await?; + } + } + } + // 3. periodically attempt to connect to any allowed routers that we + // are not connected to + _ = time::sleep(time::Duration::from_secs(3)) => { + connect_to_routers( + &our, + &our_ip, + &keypair, + &mut active_routers, + &pki, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone(), + ) + .await; + } + } + } +} + +async fn connect_to_routers( + our: &Identity, + our_ip: &str, + keypair: &Ed25519KeyPair, + active_routers: &mut HashSet, + pki: &OnchainPKI, + peers: &mut Peers, + peer_connections: &mut JoinSet<(NodeId, Option)>, + kernel_message_tx: MessageSender, +) { + for router in &our.allowed_routers { + if active_routers.contains(router) { + continue; + } + let Some(router_id) = pki.get(router) else { + continue; + }; + match init_connection(our, our_ip, router_id, keypair, None, true).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: router_id.clone(), + routing_for: false, + sender: peer_tx, + }); + println!("net: connected to router {}\r", peer_name); + peers.insert(peer_name.clone(), peer.clone()); + active_routers.insert(peer_name); + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Err(_e) => continue, + } + } +} + async fn direct_networking( - our: Arc, + our: Identity, our_ip: String, tcp: TcpListener, keypair: Arc, @@ -164,11 +308,13 @@ async fn direct_networking( let mut names: PKINames = HashMap::new(); let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); - let mut forwarding_connections = JoinSet::::new(); + let mut forwarding_connections = JoinSet::<()>::new(); + let mut pending_passthroughs: PendingPassthroughs = HashMap::new(); loop { tokio::select! { - // 1. receive messages from kernel and send out over our connections + // 1. receive messages from kernel and send out over our connections, + // making new connections as needed Some(km) = message_rx.recv() => { // got a message from kernel to send out over the network let target = &km.target.node; @@ -177,9 +323,15 @@ async fn direct_networking( if target == &our.name { match handle_local_message( &our, + &our_ip, + &keypair, km, - &peers, + &mut peers, &mut pki, + &mut peer_connections, + Some(&mut pending_passthroughs), + Some(&forwarding_connections), + None, &mut names, &kernel_message_tx, &print_tx, @@ -203,11 +355,12 @@ async fn direct_networking( // if the message is for a *direct* peer we don't have a connection with, // try to establish a connection with them if peer_id.ws_routing.is_some() { - match init_connection(&our, &our_ip, peer_id, &keypair, None).await { + match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { Ok((peer_name, direct_conn)) => { let (peer_tx, peer_rx) = unbounded_channel::(); let peer = Arc::new(Peer { identity: peer_id.clone(), + routing_for: false, sender: peer_tx, }); peers.insert(peer_name, peer.clone()); @@ -230,18 +383,20 @@ async fn direct_networking( // on chain and try to get one of them to build a proxied connection to // this node for you else { - let sent = init_connection_via_router( - &our, - &our_ip, - &keypair, - km.clone(), - peer_id, - &pki, - &mut peers, - &mut peer_connections, - kernel_message_tx.clone() - ).await; - if !sent { + let sent = time::timeout(TIMEOUT, + init_connection_via_router( + &our, + &our_ip, + &keypair, + km.clone(), + peer_id, + &pki, + &names, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone() + )).await; + if !sent.unwrap_or(false) { // none of the routers worked! println!("net: error initializing routed connection\r"); error_offline(km, &network_error_tx).await?; @@ -260,16 +415,32 @@ async fn direct_networking( // can also block based on socket_addr match accept_async(MaybeTlsStream::Plain(stream)).await { Ok(websocket) => { - let (peer_id, conn) = recv_connection(&our, &our_ip, &pki, &keypair, websocket).await?; - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id, - sender: peer_tx, - }); + let (peer_id, routing_for, conn) = + match recv_connection( + &our, + &our_ip, + &pki, + &peers, + &mut pending_passthroughs, + &keypair, + websocket).await + { + Ok(res) => res, + Err(e) => { + println!("net: recv_connection failed: {e}\r"); + continue; + } + }; // if conn is direct, add peer // if passthrough, add to our forwarding connections joinset match conn { Connection::Peer(peer_conn) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id, + routing_for, + sender: peer_tx, + }); peers.insert(peer.identity.name.clone(), peer.clone()); peer_connections.spawn(maintain_connection( peer, @@ -280,10 +451,15 @@ async fn direct_networking( } Connection::Passthrough(passthrough_conn) => { forwarding_connections.spawn(maintain_passthrough( - peer, passthrough_conn, )); } + Connection::PendingPassthrough(pending_conn) => { + pending_passthroughs.insert( + (peer_id.name.clone(), pending_conn.target.clone()), + pending_conn + ); + } } } // ignore connections we failed to accept...? @@ -311,25 +487,31 @@ async fn init_connection_via_router( km: KernelMessage, peer_id: &Identity, pki: &OnchainPKI, + names: &PKINames, peers: &mut Peers, peer_connections: &mut JoinSet<(NodeId, Option)>, kernel_message_tx: MessageSender, ) -> bool { + println!("init_connection_via_router\r"); let routers_shuffled = { let mut routers = peer_id.allowed_routers.clone(); routers.shuffle(&mut rand::thread_rng()); routers }; - for router in routers_shuffled { - let router_id = match pki.get(&router) { + for router_namehash in &routers_shuffled { + let Some(router_name) = names.get(router_namehash) else { + continue; + }; + let router_id = match pki.get(router_name) { None => continue, Some(id) => id, }; - match init_connection(&our, &our_ip, peer_id, &keypair, Some(router_id)).await { + match init_connection(&our, &our_ip, peer_id, &keypair, Some(router_id), false).await { Ok((peer_name, direct_conn)) => { let (peer_tx, peer_rx) = unbounded_channel::(); let peer = Arc::new(Peer { identity: peer_id.clone(), + routing_for: false, sender: peer_tx, }); peers.insert(peer_name, peer.clone()); @@ -398,7 +580,7 @@ async fn maintain_connection( /// match the streams /// TODO optimize performance of this -async fn maintain_passthrough(peer: Arc, mut conn: PassthroughConnection) -> NodeId { +async fn maintain_passthrough(mut conn: PassthroughConnection) { println!("maintain_passthrough\r"); loop { tokio::select! { @@ -409,7 +591,7 @@ async fn maintain_passthrough(peer: Arc, mut conn: PassthroughConnection) } _ => { println!("net: passthrough broke\r"); - return peer.identity.name.clone() + return } } }, @@ -420,7 +602,7 @@ async fn maintain_passthrough(peer: Arc, mut conn: PassthroughConnection) } _ => { println!("net: passthrough broke\r"); - return peer.identity.name.clone() + return } } }, @@ -432,34 +614,43 @@ async fn recv_connection( our: &Identity, our_ip: &str, pki: &OnchainPKI, + peers: &Peers, + pending_passthroughs: &mut PendingPassthroughs, keypair: &Ed25519KeyPair, websocket: WebSocketStream>, -) -> Result<(Identity, Connection)> { +) -> Result<(Identity, bool, Connection)> { println!("recv_connection\r"); let mut buf = vec![0u8; 65535]; let (mut noise, our_static_key) = build_responder(); let (mut write_stream, mut read_stream) = websocket.split(); - // <- e - let len = noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; + // before we begin XX handshake pattern, check first message over socket + let first_message = &ws_recv(&mut read_stream).await?; // if the first message contains a "routing request", - // we evaluate whether we want to perform routing for them - if len != 0 { - let (their_id, target_name) = validate_routing_request(&our.name, &buf, pki)?; - // TODO evaluate whether we want to perform routing for them! - // if we do, produce this thing: - return create_passthrough( + // we see if the target is someone we are actively routing for, + // and create a Passthrough connection if so. + // a Noise 'e' message with have len 32 + if first_message.len() != 32 { + let (their_id, target_name) = validate_routing_request(&our.name, &first_message, pki)?; + let (id, conn) = create_passthrough( + our, our_ip, their_id, target_name, pki, + peers, + pending_passthroughs, write_stream, read_stream, ) - .await; + .await?; + return Ok((id, false, conn)); } + // <- e + noise.read_message(first_message, &mut buf)?; + // -> e, ee, s, es send_uqbar_handshake( &our, @@ -468,6 +659,92 @@ async fn recv_connection( &mut noise, &mut buf, &mut write_stream, + false, + ) + .await?; + + // <- s, se + let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; + + // now validate this handshake payload against the QNS PKI + let their_id = pki + .get(&their_handshake.name) + .ok_or(anyhow!("unknown QNS name"))?; + validate_handshake( + &their_handshake, + noise + .get_remote_static() + .ok_or(anyhow!("noise error: missing remote pubkey"))?, + their_id, + )?; + + // Transition the state machine into transport mode now that the handshake is complete. + let noise = noise.into_transport_mode()?; + println!("handshake complete, noise session received\r"); + + // TODO if their handshake indicates they want us to proxy + // for them (aka act as a router for them) we can choose + // whether to do so here. + Ok(( + their_id.clone(), + their_handshake.proxy_request, + Connection::Peer(PeerConnection { + noise, + buf, + write_stream, + read_stream, + }), + )) +} + +async fn recv_connection_via_router( + our: &Identity, + our_ip: &str, + their_name: &str, + pki: &OnchainPKI, + keypair: &Ed25519KeyPair, + router: &Identity, +) -> Result<(Identity, PeerConnection)> { + println!("recv_connection_via_router\r"); + let mut buf = vec![0u8; 65535]; + let (mut noise, our_static_key) = build_responder(); + + let Some((ref ip, ref port)) = router.ws_routing else { + return Err(anyhow!("router has no routing information")); + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + else { + return Err(anyhow!("failed to connect to target")); + }; + let (mut write_stream, mut read_stream) = websocket.split(); + + // before beginning XX handshake pattern, send a routing request + let message = bincode::serialize(&RoutingRequest { + source: our.name.clone(), + signature: keypair + .sign([their_name, router.name.as_str()].concat().as_bytes()) + .as_ref() + .to_vec(), + target: their_name.to_string(), + protocol_version: 1, + })?; + ws_send(&mut write_stream, &message).await?; + + // <- e + noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; + + // -> e, ee, s, es + send_uqbar_handshake( + &our, + keypair, + &our_static_key, + &mut noise, + &mut buf, + &mut write_stream, + false, ) .await?; @@ -492,12 +769,12 @@ async fn recv_connection( Ok(( their_id.clone(), - Connection::Peer(PeerConnection { + PeerConnection { noise, buf, write_stream, read_stream, - }), + }, )) } @@ -507,6 +784,7 @@ async fn init_connection( peer_id: &Identity, keypair: &Ed25519KeyPair, use_router: Option<&Identity>, + proxy_request: bool, ) -> Result<(String, PeerConnection)> { println!("init_connection\r"); let mut buf = vec![0u8; 65535]; @@ -520,7 +798,7 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; @@ -533,7 +811,7 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; @@ -541,23 +819,27 @@ async fn init_connection( } }; + // if this is a routed request, before starting XX handshake pattern, send a + // routing request message over socket + if use_router.is_some() { + let message = bincode::serialize(&RoutingRequest { + source: our.name.clone(), + signature: keypair + .sign( + [&peer_id.name, use_router.unwrap().name.as_str()] + .concat() + .as_bytes(), + ) + .as_ref() + .to_vec(), + target: peer_id.name.clone(), + protocol_version: 1, + })?; + ws_send(&mut write_stream, &message).await?; + } + // -> e - let message = match use_router { - None => vec![], - Some(router_id) => { - let routing_request = RoutingRequest { - name: our.name.clone(), - signature: keypair - .sign([&peer_id.name, router_id.name.as_str()].concat().as_bytes()) - .as_ref() - .to_vec(), - target: peer_id.name.clone(), - protocol_version: 1, - }; - bincode::serialize(&routing_request)? - } - }; - let len = noise.write_message(&message, &mut buf)?; + let len = noise.write_message(&[], &mut buf)?; ws_send(&mut write_stream, &buf[..len]).await?; // <- e, ee, s, es @@ -580,6 +862,7 @@ async fn init_connection( &mut noise, &mut buf, &mut write_stream, + proxy_request, ) .await?; @@ -597,250 +880,111 @@ async fn init_connection( )) } -async fn create_passthrough( - our_ip: &str, - from_id: Identity, - to_name: NodeId, - pki: &OnchainPKI, - write_stream_1: SplitSink>, tungstenite::Message>, - read_stream_1: SplitStream>>, -) -> Result<(Identity, Connection)> { - let to_id = pki.get(&to_name).ok_or(anyhow!("unknown QNS name"))?; - let Some((ref ip, ref port)) = to_id.ws_routing else { - return Err(anyhow!("target has no routing information")); - }; - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { - return Err(anyhow!("failed to connect to target")); - }; - let (write_stream_2, read_stream_2) = websocket.split(); - - Ok(( - from_id, - Connection::Passthrough(PassthroughConnection { - write_stream_1, - read_stream_1, - write_stream_2, - read_stream_2, - }), - )) -} - -fn validate_routing_request( - our_name: &str, - buf: &[u8], - pki: &OnchainPKI, -) -> Result<(Identity, NodeId)> { - println!("validate_routing_request\r"); - let routing_request: RoutingRequest = bincode::deserialize(buf)?; - let their_id = pki - .get(&routing_request.name) - .ok_or(anyhow!("unknown QNS name"))?; - let their_networking_key = signature::UnparsedPublicKey::new( - &signature::ED25519, - hex::decode(&strip_0x(&their_id.networking_key))?, - ); - their_networking_key.verify( - &[&routing_request.target, our_name].concat().as_bytes(), - &routing_request.signature, - )?; - Ok((their_id.clone(), routing_request.target)) -} - -fn validate_handshake( - handshake: &HandshakePayload, - their_static_key: &[u8], - their_id: &Identity, -) -> Result<()> { - println!("validate_handshake\r"); - if handshake.protocol_version != 1 { - return Err(anyhow!("handshake protocol version mismatch")); - } - // verify their signature of their static key - let their_networking_key = signature::UnparsedPublicKey::new( - &signature::ED25519, - hex::decode(&strip_0x(&their_id.networking_key))?, - ); - their_networking_key.verify(their_static_key, &handshake.signature)?; - Ok(()) -} - -async fn send_uqbar_message(km: &KernelMessage, conn: &mut PeerConnection) -> Result<()> { - let serialized = bincode::serialize(km)?; - if serialized.len() > MESSAGE_MAX_SIZE as usize { - return Err(anyhow!("uqbar message too large")); - } - - let len = (serialized.len() as u32).to_be_bytes(); - let with_length_prefix = [len.to_vec(), serialized].concat(); - - for payload in with_length_prefix.chunks(65519) { - // 65535 - 16 (TAGLEN) - let len = conn.noise.write_message(payload, &mut conn.buf)?; - ws_send(&mut conn.write_stream, &conn.buf[..len]).await?; - } - Ok(()) -} - -async fn recv_uqbar_message(conn: &mut PeerConnection) -> Result { - let outer_len = conn - .noise - .read_message(&ws_recv(&mut conn.read_stream).await?, &mut conn.buf)?; - if outer_len < 4 { - return Err(anyhow!("uqbar message too small!")); - } - - let length_bytes = [conn.buf[0], conn.buf[1], conn.buf[2], conn.buf[3]]; - let msg_len = u32::from_be_bytes(length_bytes); - - let mut msg = Vec::with_capacity(msg_len as usize); - msg.extend_from_slice(&conn.buf[4..outer_len]); - - while msg.len() < msg_len as usize { - let len = conn - .noise - .read_message(&ws_recv(&mut conn.read_stream).await?, &mut conn.buf)?; - msg.extend_from_slice(&conn.buf[..len]); - } - - Ok(bincode::deserialize(&msg)?) -} - -async fn send_uqbar_handshake( - our: &Identity, - keypair: &Ed25519KeyPair, - noise_static_key: &[u8], - noise: &mut snow::HandshakeState, - buf: &mut Vec, - write_stream: &mut SplitSink>, tungstenite::Message>, -) -> Result<()> { - println!("send_uqbar_handshake\r"); - let our_hs = bincode::serialize(&HandshakePayload { - name: our.name.clone(), - signature: keypair.sign(noise_static_key).as_ref().to_vec(), - protocol_version: 1, - }) - .expect("failed to serialize handshake payload"); - - let len = noise.write_message(&our_hs, buf)?; - ws_send(write_stream, &buf[..len]).await?; - - Ok(()) -} - -async fn recv_uqbar_handshake( - noise: &mut snow::HandshakeState, - buf: &mut Vec, - read_stream: &mut SplitStream>>, -) -> Result { - println!("recv_uqbar_handshake\r"); - let len = noise.read_message(&ws_recv(read_stream).await?, buf)?; - - // from buffer, read a sequence of bytes that deserializes to the - // 1. QNS name of the sender - // 2. a signature by their published networking key that signs the - // static key they will be using for this handshake - // 3. the version number of the networking protocol (so we can upgrade it) - Ok(bincode::deserialize(&buf[..len])?) -} - -async fn ws_recv( - read_stream: &mut SplitStream>>, -) -> Result> { - let Some(Ok(tungstenite::Message::Binary(bin))) = read_stream.next().await else { - return Err(anyhow!("websocket closed")); - }; - Ok(bin) -} - -async fn ws_send( - write_stream: &mut SplitSink>, tungstenite::Message>, - msg: &[u8], -) -> Result<()> { - write_stream.send(tungstenite::Message::binary(msg)).await?; - Ok(()) -} - -fn build_responder() -> (snow::HandshakeState, Vec) { - let builder: snow::Builder<'_> = snow::Builder::new(PARAMS.clone()); - let keypair = builder - .generate_keypair() - .expect("net: couldn't generate keypair?"); - ( - builder - .local_private_key(&keypair.private) - .build_responder() - .expect("net: couldn't build responder?"), - keypair.public, - ) -} - -fn build_initiator() -> (snow::HandshakeState, Vec) { - let builder: snow::Builder<'_> = snow::Builder::new(PARAMS.clone()); - let keypair = builder - .generate_keypair() - .expect("net: couldn't generate keypair?"); - ( - builder - .local_private_key(&keypair.private) - .build_initiator() - .expect("net: couldn't build responder?"), - keypair.public, - ) -} - -fn make_ws_url(our_ip: &str, ip: &str, port: &u16) -> Result { - // if we have the same public IP as target, route locally, - // otherwise they will appear offline due to loopback stuff - let ip = if our_ip == ip { "localhost" } else { ip }; - match url::Url::parse(&format!("ws://{}:{}/ws", ip, port)) { - Ok(v) => Ok(v), - Err(_) => Err(SendErrorKind::Offline), - } -} - -async fn error_offline(km: KernelMessage, network_error_tx: &NetworkErrorSender) -> Result<()> { - network_error_tx - .send(WrappedSendError { - id: km.id, - source: km.source, - error: SendError { - kind: SendErrorKind::Offline, - target: km.target, - message: km.message, - payload: km.payload, - }, - }) - .await?; - Ok(()) -} - -fn strip_0x(s: &str) -> String { - if s.starts_with("0x") { - s[2..].to_string() - } else { - s.to_string() - } -} - /// net module only handles incoming local requests, will never return a response async fn handle_local_message( our: &Identity, + our_ip: &str, + keypair: &Ed25519KeyPair, km: KernelMessage, - peers: &Peers, + peers: &mut Peers, pki: &mut OnchainPKI, + peer_connections: &mut JoinSet<(NodeId, Option)>, + pending_passthroughs: Option<&mut PendingPassthroughs>, + forwarding_connections: Option<&JoinSet<()>>, + active_routers: Option<&HashSet>, names: &mut PKINames, kernel_message_tx: &MessageSender, print_tx: &PrintSender, ) -> Result<()> { + println!("handle_local_message\r"); let ipc = match km.message { - Message::Response(_) => return Ok(()), Message::Request(request) => request.ipc, + Message::Response((response, _context)) => { + // these are received as a router, when we send ConnectionRequests + // to a node we do routing for. + match serde_json::from_slice::(&response.ipc)? { + NetResponses::Attempting(_) => { + // TODO anything here? + } + NetResponses::Rejected(to) => { + // drop from our pending map + // this will drop the socket, causing initiator to see it as failed + pending_passthroughs + .ok_or(anyhow!("got net response as non-router"))? + .remove(&(to, km.source.node)); + } + } + return Ok(()); + } }; if km.source.node != our.name { + if let Ok(act) = serde_json::from_slice::(&ipc) { + match act { + NetActions::QnsBatchUpdate(_) | NetActions::QnsUpdate(_) => { + // for now, we don't get these from remote. + } + NetActions::ConnectionRequest(from) => { + // someone wants to open a passthrough with us through a router! + // if we are an indirect node, and source is one of our routers, + // respond by attempting to init a matching passthrough. + // TODO can discriminate more here.. + if our.allowed_routers.contains(&km.source.node) { + let Ok((peer_id, peer_conn)) = time::timeout(TIMEOUT, + recv_connection_via_router( + our, + our_ip, + &from, + pki, + keypair, + &peers + .get(&km.source.node) + .ok_or(anyhow!("unknown router"))? + .identity, + )).await? else { + return Err(anyhow!("someone tried to connect to us but it timed out")) + }; + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id, + routing_for: false, + sender: peer_tx, + }); + peers.insert(peer.identity.name.clone(), peer.clone()); + peer_connections.spawn(maintain_connection( + peer, + peer_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } else { + kernel_message_tx + .send(KernelMessage { + id: km.id, + source: Address { + node: our.name.clone(), + process: ProcessId::from_str("net:sys:uqbar").unwrap(), + }, + target: km.rsvp.unwrap_or(km.source), + rsvp: None, + message: Message::Response(( + Response { + inherit: false, + ipc: serde_json::to_vec(&NetResponses::Rejected(from))?, + metadata: None, + }, + None, + )), + payload: None, + signed_capabilities: None, + }) + .await?; + } + } + } + return Ok(()); + }; + // if we can't parse this to a netaction, treat it as a hello and print it // respond to a text message with a simple "delivered" response print_tx .send(Printout { @@ -875,7 +1019,7 @@ async fn handle_local_message( .await?; Ok(()) } else { - // available commands: "peers", "QnsUpdate" (see qns_indexer module) + // available commands: "peers", "pki", "names", "diagnostics" // first parse as raw string, then deserialize to NetActions object match std::str::from_utf8(&ipc) { Ok("peers") => { @@ -902,6 +1046,68 @@ async fn handle_local_message( }) .await?; } + Ok("diagnostics") => { + print_tx + .send(Printout { + verbosity: 0, + content: format!("our Identity: {:#?}", our), + }) + .await?; + print_tx + .send(Printout { + verbosity: 0, + content: format!("we have connections with peers: {:#?}", peers.keys()), + }) + .await?; + print_tx + .send(Printout { + verbosity: 0, + content: format!("we have {} entries in the PKI", pki.len()), + }) + .await?; + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} open peer connections", + peer_connections.len() + ), + }) + .await?; + if pending_passthroughs.is_some() { + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} pending passthrough connections", + pending_passthroughs.unwrap().len() + ), + }) + .await?; + } + if forwarding_connections.is_some() { + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} open passthrough connections", + forwarding_connections.unwrap().len() + ), + }) + .await?; + } + if active_routers.is_some() { + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} active routers", + active_routers.unwrap().len() + ), + }) + .await?; + } + } _ => { let Ok(act) = serde_json::from_slice::(&ipc) else { print_tx @@ -913,6 +1119,9 @@ async fn handle_local_message( return Ok(()); }; match act { + NetActions::ConnectionRequest(_) => { + // we shouldn't receive these from ourselves. + } NetActions::QnsUpdate(log) => { print_tx .send(Printout { diff --git a/src/net2/types.rs b/src/net2/types.rs new file mode 100644 index 00000000..4141e718 --- /dev/null +++ b/src/net2/types.rs @@ -0,0 +1,115 @@ +use crate::types::*; +use futures::stream::{SplitSink, SplitStream}; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, sync::Arc}; +use tokio::net::TcpStream; +use tokio::sync::mpsc::UnboundedSender; +use tokio_tungstenite::{tungstenite, MaybeTlsStream, WebSocketStream}; + +/// Sent to a node when you want to connect directly to them. +/// Sent in the 'e, ee, s, es' and 's, se' phases of XX noise protocol pattern. +#[derive(Debug, Deserialize, Serialize)] +pub struct HandshakePayload { + pub name: NodeId, + // signature is created by their networking key, of their static key + // someone could reuse this signature, but then they will be unable + // to encrypt messages to us. + pub signature: Vec, + /// Set to true when you want them to act as a router for you, sending + /// messages from potentially many remote sources over this connection, + /// including from the router itself. + /// This is not relevant in a handshake sent from the receiver side. + pub proxy_request: bool, + pub protocol_version: u8, +} + +/// Sent to a node when you want them to connect you to an indirect node. +/// If the receiver of the request has an open connection to your target, +/// and is willing, they will send a message to the target prompting them +/// to build the other side of the connection, at which point they will +/// hold open a Passthrough for you two. +/// +/// Alternatively, if the receiver does not have an open connection but the +/// target is a direct node, they can create a Passthrough for you two if +/// they are willing to proxy for you. +/// +/// Sent in the 'e' phase of XX noise protocol pattern. +#[derive(Debug, Deserialize, Serialize)] +pub struct RoutingRequest { + pub source: NodeId, + // signature is created by their networking key, of the [target, router name].concat() + // someone could reuse this signature, and TODO need to make sure that's useless. + pub signature: Vec, + pub target: NodeId, + pub protocol_version: u8, +} + +pub enum Connection { + Peer(PeerConnection), + Passthrough(PassthroughConnection), + PendingPassthrough(PendingPassthroughConnection), +} + +pub struct PeerConnection { + pub noise: snow::TransportState, + pub buf: Vec, + pub write_stream: SplitSink>, tungstenite::Message>, + pub read_stream: SplitStream>>, +} + +pub struct PassthroughConnection { + pub write_stream_1: SplitSink>, tungstenite::Message>, + pub read_stream_1: SplitStream>>, + pub write_stream_2: SplitSink>, tungstenite::Message>, + pub read_stream_2: SplitStream>>, +} + +pub struct PendingPassthroughConnection { + pub target: NodeId, + pub write_stream: SplitSink>, tungstenite::Message>, + pub read_stream: SplitStream>>, +} + +// TODO upgrade from hashmaps +pub type Peers = HashMap>; +pub type PKINames = HashMap; // TODO maybe U256 to String +pub type OnchainPKI = HashMap; +pub type PendingPassthroughs = HashMap<(NodeId, NodeId), PendingPassthroughConnection>; + +pub struct Peer { + pub identity: Identity, + /// If true, we are routing for them and have a RoutingClientConnection + /// associated with them. We can send them prompts to establish Passthroughs. + pub routing_for: bool, + pub sender: UnboundedSender, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum NetActions { + /// Received from a router of ours when they have a new pending passthrough for us. + /// We should respond (if we desire) by using them to initialize a routed connection + /// with the NodeId given. + ConnectionRequest(NodeId), + /// can only receive from trusted source, for now just ourselves locally, + /// in the future could get from remote provider + QnsUpdate(QnsUpdate), + QnsBatchUpdate(Vec), +} + +/// For now, only sent in response to a ConnectionRequest +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum NetResponses { + Attempting(NodeId), + Rejected(NodeId), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct QnsUpdate { + pub name: String, // actual username / domain name + pub owner: String, + pub node: String, // hex namehash of node + pub public_key: String, + pub ip: String, + pub port: u16, + pub routers: Vec, +} diff --git a/src/net2/utils.rs b/src/net2/utils.rs new file mode 100644 index 00000000..d5d4a7d1 --- /dev/null +++ b/src/net2/utils.rs @@ -0,0 +1,301 @@ +use crate::net2::{types::*, MESSAGE_MAX_SIZE, TIMEOUT}; +use crate::types::*; +use anyhow::{anyhow, Result}; +use futures::stream::{SplitSink, SplitStream}; +use futures::{SinkExt, StreamExt}; +use ring::signature::{self, Ed25519KeyPair}; +use snow::params::NoiseParams; +use tokio::net::TcpStream; +use tokio::time::timeout; +use tokio_tungstenite::{connect_async, tungstenite, MaybeTlsStream, WebSocketStream}; + +lazy_static::lazy_static! { + static ref PARAMS: NoiseParams = "Noise_XX_25519_ChaChaPoly_BLAKE2s" + .parse() + .expect("net: couldn't build noise params?"); +} + +pub async fn create_passthrough( + our: &Identity, + our_ip: &str, + from_id: Identity, + to_name: NodeId, + pki: &OnchainPKI, + peers: &Peers, + pending_passthroughs: &mut PendingPassthroughs, + write_stream_1: SplitSink>, tungstenite::Message>, + read_stream_1: SplitStream>>, +) -> Result<(Identity, Connection)> { + println!("create_passthrough\r"); + // if the target has already generated a pending passthrough for this source, + // immediately match them + println!("current: {:?}\r", pending_passthroughs.keys()); + println!("this one: {:?}\r", (to_name.clone(), from_id.name.clone())); + if let Some(pending) = pending_passthroughs.remove(&(to_name.clone(), from_id.name.clone())) { + return Ok(( + from_id, + Connection::Passthrough(PassthroughConnection { + write_stream_1, + read_stream_1, + write_stream_2: pending.write_stream, + read_stream_2: pending.read_stream, + }), + )); + } + let to_id = pki.get(&to_name).ok_or(anyhow!("unknown QNS name"))?; + let Some((ref ip, ref port)) = to_id.ws_routing else { + // create passthrough to indirect node that we do routing for + // + let target_peer = peers.get(&to_name).ok_or(anyhow!("can't route to that indirect node"))?; + if !target_peer.routing_for { + return Err(anyhow!("we don't route for that indirect node")) + } + // send their net:sys:uqbar process a message, notifying it to create a *matching* + // passthrough request, which we can pair with this pending one. + target_peer.sender.send(KernelMessage { + id: rand::random(), + source: Address { node: our.name.clone(), process: ProcessId::from_str("net:sys:uqbar").unwrap() }, + target: Address { node: to_name.clone(), process: ProcessId::from_str("net:sys:uqbar").unwrap() }, + rsvp: None, + message: Message::Request(Request { + inherit: false, + expects_response: Some(5), + ipc: serde_json::to_vec(&NetActions::ConnectionRequest(from_id.name.clone()))?, + metadata: None, + }), + payload: None, + signed_capabilities: None, + })?; + + return Ok(( + from_id, + Connection::PendingPassthrough(PendingPassthroughConnection { + target: to_name, + write_stream: write_stream_1, + read_stream: read_stream_1, + }) + )); + }; + // create passthrough to direct node + // + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { + return Err(anyhow!("failed to connect to target")); + }; + let (write_stream_2, read_stream_2) = websocket.split(); + + Ok(( + from_id, + Connection::Passthrough(PassthroughConnection { + write_stream_1, + read_stream_1, + write_stream_2, + read_stream_2, + }), + )) +} + +pub fn validate_routing_request( + our_name: &str, + buf: &[u8], + pki: &OnchainPKI, +) -> Result<(Identity, NodeId)> { + println!("validate_routing_request\r"); + let routing_request: RoutingRequest = bincode::deserialize(buf)?; + println!("routing request: {:?}\r", routing_request); + let their_id = pki + .get(&routing_request.source) + .ok_or(anyhow!("unknown QNS name"))?; + let their_networking_key = signature::UnparsedPublicKey::new( + &signature::ED25519, + hex::decode(&strip_0x(&their_id.networking_key))?, + ); + their_networking_key.verify( + &[&routing_request.target, our_name].concat().as_bytes(), + &routing_request.signature, + )?; + if routing_request.target == routing_request.source { + return Err(anyhow!("can't route to self")) + } + Ok((their_id.clone(), routing_request.target)) +} + +pub fn validate_handshake( + handshake: &HandshakePayload, + their_static_key: &[u8], + their_id: &Identity, +) -> Result<()> { + println!("validate_handshake\r"); + if handshake.protocol_version != 1 { + return Err(anyhow!("handshake protocol version mismatch")); + } + // verify their signature of their static key + let their_networking_key = signature::UnparsedPublicKey::new( + &signature::ED25519, + hex::decode(&strip_0x(&their_id.networking_key))?, + ); + their_networking_key.verify(their_static_key, &handshake.signature)?; + Ok(()) +} + +pub async fn send_uqbar_message(km: &KernelMessage, conn: &mut PeerConnection) -> Result<()> { + let serialized = bincode::serialize(km)?; + if serialized.len() > MESSAGE_MAX_SIZE as usize { + return Err(anyhow!("uqbar message too large")); + } + + let len = (serialized.len() as u32).to_be_bytes(); + let with_length_prefix = [len.to_vec(), serialized].concat(); + + for payload in with_length_prefix.chunks(65519) { + // 65535 - 16 (TAGLEN) + let len = conn.noise.write_message(payload, &mut conn.buf)?; + ws_send(&mut conn.write_stream, &conn.buf[..len]).await?; + } + Ok(()) +} + +pub async fn recv_uqbar_message(conn: &mut PeerConnection) -> Result { + let outer_len = conn + .noise + .read_message(&ws_recv(&mut conn.read_stream).await?, &mut conn.buf)?; + if outer_len < 4 { + return Err(anyhow!("uqbar message too small!")); + } + + let length_bytes = [conn.buf[0], conn.buf[1], conn.buf[2], conn.buf[3]]; + let msg_len = u32::from_be_bytes(length_bytes); + + let mut msg = Vec::with_capacity(msg_len as usize); + msg.extend_from_slice(&conn.buf[4..outer_len]); + + while msg.len() < msg_len as usize { + let len = conn + .noise + .read_message(&ws_recv(&mut conn.read_stream).await?, &mut conn.buf)?; + msg.extend_from_slice(&conn.buf[..len]); + } + + Ok(bincode::deserialize(&msg)?) +} + +pub async fn send_uqbar_handshake( + our: &Identity, + keypair: &Ed25519KeyPair, + noise_static_key: &[u8], + noise: &mut snow::HandshakeState, + buf: &mut Vec, + write_stream: &mut SplitSink>, tungstenite::Message>, + proxy_request: bool, +) -> Result<()> { + println!("send_uqbar_handshake\r"); + let our_hs = bincode::serialize(&HandshakePayload { + name: our.name.clone(), + signature: keypair.sign(noise_static_key).as_ref().to_vec(), + protocol_version: 1, + proxy_request, + }) + .expect("failed to serialize handshake payload"); + + let len = noise.write_message(&our_hs, buf)?; + ws_send(write_stream, &buf[..len]).await?; + + Ok(()) +} + +pub async fn recv_uqbar_handshake( + noise: &mut snow::HandshakeState, + buf: &mut Vec, + read_stream: &mut SplitStream>>, +) -> Result { + println!("recv_uqbar_handshake\r"); + let len = noise.read_message(&ws_recv(read_stream).await?, buf)?; + + // from buffer, read a sequence of bytes that deserializes to the + // 1. QNS name of the sender + // 2. a signature by their published networking key that signs the + // static key they will be using for this handshake + // 3. the version number of the networking protocol (so we can upgrade it) + Ok(bincode::deserialize(&buf[..len])?) +} + +pub async fn ws_recv( + read_stream: &mut SplitStream>>, +) -> Result> { + let Some(Ok(tungstenite::Message::Binary(bin))) = read_stream.next().await else { + return Err(anyhow!("websocket closed")); + }; + Ok(bin) +} + +pub async fn ws_send( + write_stream: &mut SplitSink>, tungstenite::Message>, + msg: &[u8], +) -> Result<()> { + write_stream.send(tungstenite::Message::binary(msg)).await?; + Ok(()) +} + +pub fn build_responder() -> (snow::HandshakeState, Vec) { + let builder: snow::Builder<'_> = snow::Builder::new(PARAMS.clone()); + let keypair = builder + .generate_keypair() + .expect("net: couldn't generate keypair?"); + ( + builder + .local_private_key(&keypair.private) + .build_responder() + .expect("net: couldn't build responder?"), + keypair.public, + ) +} + +pub fn build_initiator() -> (snow::HandshakeState, Vec) { + let builder: snow::Builder<'_> = snow::Builder::new(PARAMS.clone()); + let keypair = builder + .generate_keypair() + .expect("net: couldn't generate keypair?"); + ( + builder + .local_private_key(&keypair.private) + .build_initiator() + .expect("net: couldn't build responder?"), + keypair.public, + ) +} + +pub fn make_ws_url(our_ip: &str, ip: &str, port: &u16) -> Result { + // if we have the same public IP as target, route locally, + // otherwise they will appear offline due to loopback stuff + let ip = if our_ip == ip { "localhost" } else { ip }; + match url::Url::parse(&format!("ws://{}:{}/ws", ip, port)) { + Ok(v) => Ok(v), + Err(_) => Err(SendErrorKind::Offline), + } +} + +pub async fn error_offline(km: KernelMessage, network_error_tx: &NetworkErrorSender) -> Result<()> { + network_error_tx + .send(WrappedSendError { + id: km.id, + source: km.source, + error: SendError { + kind: SendErrorKind::Offline, + target: km.target, + message: km.message, + payload: km.payload, + }, + }) + .await?; + Ok(()) +} + +fn strip_0x(s: &str) -> String { + if s.starts_with("0x") { + s[2..].to_string() + } else { + s.to_string() + } +} diff --git a/src/register b/src/register index 63cd0448..be610be7 160000 --- a/src/register +++ b/src/register @@ -1 +1 @@ -Subproject commit 63cd04480aefe8c9a3c185ee5dba4e9b6cdbddf8 +Subproject commit be610be7e3a11d9dfa67b3050ed5e9e62f7e0820 diff --git a/src/register.rs b/src/register.rs index ae57fc3d..9b55fa68 100644 --- a/src/register.rs +++ b/src/register.rs @@ -159,12 +159,11 @@ async fn handle_boot( ) -> Result { our.name = info.username; - // println!("handle_boot: info.direct: {}\r", info.direct); - // if info.direct { - // our.allowed_routers = vec![]; - // } else { - // our.ws_routing = None; - // } + if info.direct { + our.allowed_routers = vec![]; + } else { + our.ws_routing = None; + } // if keyfile was not present in node and is present from user upload if encoded_keyfile.is_empty() && !info.keyfile.clone().is_empty() { @@ -257,11 +256,19 @@ async fn handle_info( // TODO: if IP is localhost, assign a router... let ws_port = http_server::find_open_port(9000).await.unwrap(); + // this is NOT our real identity. it's stuff we give to the frontend + // to match on let our = Identity { networking_key: format!("0x{}", public_key), name: String::new(), ws_routing: Some((ip.clone(), ws_port)), - allowed_routers: vec![], + allowed_routers: vec![ + "testnode101.uq".into(), + "testnode102.uq".into(), + // "uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(), + // "uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(), + // "uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(), + ], }; *our_arc.lock().unwrap() = Some(our.clone()); From 901adc3dc7f8be5dbd10a049bdc4196425831a7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:41:48 +0000 Subject: [PATCH 14/40] Format Rust code using rustfmt --- src/net2/mod.rs | 41 +++++++++++++++++++++++------------------ src/net2/utils.rs | 20 ++++++++++++++------ 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/net2/mod.rs b/src/net2/mod.rs index 97e668df..7894c041 100644 --- a/src/net2/mod.rs +++ b/src/net2/mod.rs @@ -715,8 +715,7 @@ async fn recv_connection_via_router( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await - else { + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; let (mut write_stream, mut read_stream) = websocket.split(); @@ -798,7 +797,8 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + let Ok(Ok((websocket, _response))) = + time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; @@ -811,7 +811,8 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + let Ok(Ok((websocket, _response))) = + time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; @@ -930,20 +931,24 @@ async fn handle_local_message( // respond by attempting to init a matching passthrough. // TODO can discriminate more here.. if our.allowed_routers.contains(&km.source.node) { - let Ok((peer_id, peer_conn)) = time::timeout(TIMEOUT, - recv_connection_via_router( - our, - our_ip, - &from, - pki, - keypair, - &peers - .get(&km.source.node) - .ok_or(anyhow!("unknown router"))? - .identity, - )).await? else { - return Err(anyhow!("someone tried to connect to us but it timed out")) - }; + let Ok((peer_id, peer_conn)) = time::timeout( + TIMEOUT, + recv_connection_via_router( + our, + our_ip, + &from, + pki, + keypair, + &peers + .get(&km.source.node) + .ok_or(anyhow!("unknown router"))? + .identity, + ), + ) + .await? + else { + return Err(anyhow!("someone tried to connect to us but it timed out")); + }; let (peer_tx, peer_rx) = unbounded_channel::(); let peer = Arc::new(Peer { identity: peer_id, diff --git a/src/net2/utils.rs b/src/net2/utils.rs index d5d4a7d1..addf5bbb 100644 --- a/src/net2/utils.rs +++ b/src/net2/utils.rs @@ -46,16 +46,24 @@ pub async fn create_passthrough( let Some((ref ip, ref port)) = to_id.ws_routing else { // create passthrough to indirect node that we do routing for // - let target_peer = peers.get(&to_name).ok_or(anyhow!("can't route to that indirect node"))?; + let target_peer = peers + .get(&to_name) + .ok_or(anyhow!("can't route to that indirect node"))?; if !target_peer.routing_for { - return Err(anyhow!("we don't route for that indirect node")) + return Err(anyhow!("we don't route for that indirect node")); } // send their net:sys:uqbar process a message, notifying it to create a *matching* // passthrough request, which we can pair with this pending one. target_peer.sender.send(KernelMessage { id: rand::random(), - source: Address { node: our.name.clone(), process: ProcessId::from_str("net:sys:uqbar").unwrap() }, - target: Address { node: to_name.clone(), process: ProcessId::from_str("net:sys:uqbar").unwrap() }, + source: Address { + node: our.name.clone(), + process: ProcessId::from_str("net:sys:uqbar").unwrap(), + }, + target: Address { + node: to_name.clone(), + process: ProcessId::from_str("net:sys:uqbar").unwrap(), + }, rsvp: None, message: Message::Request(Request { inherit: false, @@ -73,7 +81,7 @@ pub async fn create_passthrough( target: to_name, write_stream: write_stream_1, read_stream: read_stream_1, - }) + }), )); }; // create passthrough to direct node @@ -117,7 +125,7 @@ pub fn validate_routing_request( &routing_request.signature, )?; if routing_request.target == routing_request.source { - return Err(anyhow!("can't route to self")) + return Err(anyhow!("can't route to self")); } Ok((their_id.clone(), routing_request.target)) } From 0e9543f1b706c8852ca302347d280e56b0191595 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Tue, 31 Oct 2023 15:43:19 -0400 Subject: [PATCH 15/40] replace old net with net2 --- src/main.rs | 4 +- src/net/connections.rs | 599 ------------ src/net/mod.rs | 1853 ++++++++++++++++++++---------------- src/net/types.rs | 141 ++- src/{net2 => net}/utils.rs | 2 +- src/net2/mod.rs | 1186 ----------------------- src/net2/types.rs | 115 --- 7 files changed, 1134 insertions(+), 2766 deletions(-) delete mode 100644 src/net/connections.rs rename src/{net2 => net}/utils.rs (99%) delete mode 100644 src/net2/mod.rs delete mode 100644 src/net2/types.rs diff --git a/src/main.rs b/src/main.rs index c5685f89..c65a7e0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ mod http_client; mod http_server; mod kernel; mod keygen; -mod net2; +mod net; mod register; mod terminal; mod types; @@ -254,7 +254,7 @@ async fn main() { vfs_message_sender, encryptor_sender, )); - tasks.spawn(net2::networking( + tasks.spawn(net::networking( our.clone(), our_ip.to_string(), networking_keypair_arc.clone(), diff --git a/src/net/connections.rs b/src/net/connections.rs deleted file mode 100644 index d6b70f85..00000000 --- a/src/net/connections.rs +++ /dev/null @@ -1,599 +0,0 @@ -use crate::net::*; -use chacha20poly1305::{ - aead::{Aead, AeadCore, KeyInit, OsRng}, - XChaCha20Poly1305, XNonce, -}; -use elliptic_curve::ecdh::SharedSecret; -use futures::{SinkExt, StreamExt}; -use ring::signature::Ed25519KeyPair; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use tokio::task::JoinHandle; -use tokio_tungstenite::tungstenite; - -pub async fn build_connection( - our: Identity, - keypair: Arc, - pki: OnchainPKI, - keys: PeerKeys, - peers: Peers, - websocket: WebSocket, - kernel_message_tx: MessageSender, - net_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, - with: Option, -) -> ( - UnboundedSender<(NetworkMessage, Option)>, - JoinHandle>, -) { - // println!("building new connection\r"); - let (message_tx, message_rx) = unbounded_channel::<(NetworkMessage, Option)>(); - let handle = tokio::spawn(maintain_connection( - our, - with, - keypair, - pki, - keys, - peers, - websocket, - message_tx.clone(), - message_rx, - kernel_message_tx, - net_message_tx, - network_error_tx, - )); - return (message_tx, handle); -} - -/// Keeps a connection alive and handles sending and receiving of NetworkMessages through it. -/// TODO add a keepalive PING/PONG system -/// TODO kill this after a certain amount of inactivity -pub async fn maintain_connection( - our: Identity, - with: Option, - keypair: Arc, - pki: OnchainPKI, - keys: PeerKeys, - peers: Peers, - websocket: WebSocket, - message_tx: UnboundedSender<(NetworkMessage, Option)>, - mut message_rx: UnboundedReceiver<(NetworkMessage, Option)>, - kernel_message_tx: MessageSender, - net_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, -) -> Option { - // let conn_id: u64 = rand::random(); - // println!("maintaining connection {conn_id}\r"); - - // accept messages on the websocket in one task, and send messages in another - let (mut write_stream, mut read_stream) = websocket.split(); - - let (forwarding_ack_tx, mut forwarding_ack_rx) = unbounded_channel::(); - // manage outstanding ACKs from messages sent over the connection - // TODO replace with more performant data structure - let ack_map = Arc::new(RwLock::new(HashMap::::new())); - let sender_ack_map = ack_map.clone(); - - let last_pong = Arc::new(RwLock::new(tokio::time::Instant::now())); - let ping_last_pong = last_pong.clone(); - let ping_tx = message_tx.clone(); - - // Ping/Pong keepalive task - let ping_task = tokio::spawn(async move { - loop { - tokio::time::sleep(tokio::time::Duration::from_secs(30)).await; - if ping_last_pong.read().await.elapsed() > tokio::time::Duration::from_secs(60) { - break; - } - if let Err(_) = ping_tx.send((NetworkMessage::Ping, None)) { - // Failed to send Ping, kill the connection - break; - } - } - }); - - let forwarder_message_tx = message_tx.clone(); - let ack_forwarder = tokio::spawn(async move { - while let Some(result) = forwarding_ack_rx.recv().await { - match result { - Ok(NetworkMessage::Ack(id)) => { - // println!("net: got forwarding ack for message {}\r", id); - forwarder_message_tx - .send((NetworkMessage::Ack(id), None)) - .unwrap(); - } - Ok(NetworkMessage::Nack(id)) => { - // println!("net: got forwarding nack for message {}\r", id); - forwarder_message_tx - .send((NetworkMessage::Nack(id), None)) - .unwrap(); - } - Ok(NetworkMessage::HandshakeAck(handshake)) => { - // println!( - // "net: got forwarding handshakeAck for message {}\r", - // handshake.id - // ); - forwarder_message_tx - .send((NetworkMessage::HandshakeAck(handshake), None)) - .unwrap(); - } - Err((message_id, _e)) => { - // println!("net: got forwarding error from ack_rx: {:?}\r", e); - // what do we do here? - forwarder_message_tx - .send((NetworkMessage::Nack(message_id), None)) - .unwrap(); - } - _ => { - // println!("net: weird none ack\r"); - } - } - } - }); - - // receive messages from over the websocket and route them to the correct peer handler, - // or create it, if necessary. - let ws_receiver = tokio::spawn(async move { - while let Some(incoming) = read_stream.next().await { - let Ok(tungstenite::Message::Binary(bin)) = incoming else { - if let Ok(tungstenite::Message::Ping(_)) = incoming { - // let _ = write_stream.send(tungstenite::Message::Pong(vec![])).await; - } - continue; - }; - // TODO use a language-netural serialization format here! - let Ok(net_message) = bincode::deserialize::(&bin) else { - // just kill the connection if we get a non-Uqbar message - break; - }; - match net_message { - NetworkMessage::Pong => { - *last_pong.write().await = tokio::time::Instant::now(); - continue; - } - NetworkMessage::Ping => { - // respond with a Pong - let _ = message_tx.send((NetworkMessage::Pong, None)); - continue; - } - NetworkMessage::Ack(id) => { - let Some(result_tx) = ack_map.write().await.remove(&id) else { - // println!("conn {conn_id}: got unexpected Ack {id}\r"); - continue; - }; - // println!("conn {conn_id}: got Ack {id}\r"); - let _ = result_tx.send(Ok(net_message)); - continue; - } - NetworkMessage::Nack(id) => { - let Some(result_tx) = ack_map.write().await.remove(&id) else { - // println!("net: got unexpected Nack\r"); - continue; - }; - let _ = result_tx.send(Ok(net_message)); - continue; - } - NetworkMessage::Msg { - ref id, - ref from, - ref to, - ref contents, - } => { - // println!("conn {conn_id}: handling msg {id}\r"); - // if the message is *directed to us*, try to handle with the - // matching peer handler "decrypter". - // - if to == &our.name { - // if we have the peer, send the message to them. - if let Some(peer) = peers.read().await.get(from) { - let _ = peer - .decrypter - .send((contents.to_owned(), forwarding_ack_tx.clone())); - continue; - } - // if we don't have the peer, see if we have the keys to create them. - // if we don't have their keys, throw a nack. - if let Some((peer_id, secret)) = keys.read().await.get(from) { - let new_peer = create_new_peer( - our.clone(), - peer_id.clone(), - peers.clone(), - keys.clone(), - secret.clone(), - message_tx.clone(), - kernel_message_tx.clone(), - net_message_tx.clone(), - network_error_tx.clone(), - ); - let _ = new_peer - .decrypter - .send((contents.to_owned(), forwarding_ack_tx.clone())); - peers.write().await.insert(peer_id.name.clone(), new_peer); - } else { - // println!("net: nacking message {id}\r"); - message_tx.send((NetworkMessage::Nack(*id), None)).unwrap(); - } - } else { - // if the message is *directed to someone else*, try to handle - // with the matching peer handler "sender". - // - if let Some(peer) = peers.read().await.get(to) { - let id = *id; - let to = to.clone(); - match peer.sender.send(( - PeerMessage::Net(net_message), - Some(forwarding_ack_tx.clone()), - )) { - Ok(_) => {} - Err(_) => { - peers.write().await.remove(&to); - message_tx.send((NetworkMessage::Nack(id), None)).unwrap(); - } - } - } else { - // if we don't have the peer, throw a nack. - // println!("net: nacking message with id {id}\r"); - message_tx.send((NetworkMessage::Nack(*id), None)).unwrap(); - } - } - } - NetworkMessage::Handshake(ref handshake) => { - // when we get a handshake, if we are the target, - // 1. verify it against the PKI - // 2. send a response handshakeAck - // 3. create a Peer and save, replacing old one if it existed - // as long as we are the target, we also get to kill this connection - // if the handshake is invalid, since it must be directly "to" us. - if handshake.target == our.name { - let Some(peer_id) = pki.read().await.get(&handshake.from).cloned() else { - // println!( - // "net: failed handshake with unknown node {}\r", - // handshake.from - // ); - message_tx - .send((NetworkMessage::Nack(handshake.id), None)) - .unwrap(); - break; - }; - let their_ephemeral_pk = match validate_handshake(&handshake, &peer_id) { - Ok(pk) => pk, - Err(e) => { - println!("net: invalid handshake from {}: {}\r", handshake.from, e); - message_tx - .send((NetworkMessage::Nack(handshake.id), None)) - .unwrap(); - break; - } - }; - let (secret, handshake) = make_secret_and_handshake( - &our, - keypair.clone(), - &handshake.from, - Some(handshake.id), - ); - message_tx - .send((NetworkMessage::HandshakeAck(handshake), None)) - .unwrap(); - let secret = Arc::new(secret.diffie_hellman(&their_ephemeral_pk)); - // save the handshake to our Keys map - keys.write() - .await - .insert(peer_id.name.clone(), (peer_id.clone(), secret.clone())); - let new_peer = create_new_peer( - our.clone(), - peer_id.clone(), - peers.clone(), - keys.clone(), - secret, - message_tx.clone(), - kernel_message_tx.clone(), - net_message_tx.clone(), - network_error_tx.clone(), - ); - // we might be replacing an old peer, so we need to remove it first - // we can't rely on the hashmap for this, because the dropped peer - // will trigger a drop of the sender, which will kill the peer_handler - peers.write().await.remove(&peer_id.name); - peers.write().await.insert(peer_id.name.clone(), new_peer); - } else { - // if we are NOT the target, - // try to send it to the matching peer handler "sender" - if let Some(peer) = peers.read().await.get(&handshake.target) { - let _ = peer.sender.send(( - PeerMessage::Net(net_message), - Some(forwarding_ack_tx.clone()), - )); - } else { - // if we don't have the peer, throw a nack. - // println!("net: nacking handshake with id {}\r", handshake.id); - message_tx - .send((NetworkMessage::Nack(handshake.id), None)) - .unwrap(); - } - } - } - NetworkMessage::HandshakeAck(ref handshake) => { - let Some(result_tx) = ack_map.write().await.remove(&handshake.id) else { - continue; - }; - let _ = result_tx.send(Ok(net_message)); - } - } - } - }); - - tokio::select! { - _ = ws_receiver => { - // println!("ws_receiver died\r"); - }, - _ = ack_forwarder => { - // println!("ack_forwarder died\r"); - }, - _ = ping_task => { - // println!("ping_task died\r"); - }, - // receive messages we would like to send to peers along this connection - // and send them to the websocket - _ = async { - while let Some((message, result_tx)) = message_rx.recv().await { - // TODO use a language-netural serialization format here! - if let Ok(bytes) = bincode::serialize::(&message) { - match &message { - NetworkMessage::Msg { id, .. } => { - // println!("conn {conn_id}: piping msg {id}\r"); - sender_ack_map.write().await.insert(*id, result_tx.unwrap()); - } - NetworkMessage::Handshake(h) => { - sender_ack_map.write().await.insert(h.id, result_tx.unwrap()); - } - _ => {} - } - match write_stream.send(tungstenite::Message::Binary(bytes)).await { - Ok(()) => {} - Err(e) => { - // println!("net: send error: {:?}\r", e); - let id = match &message { - NetworkMessage::Msg { id, .. } => id, - NetworkMessage::Handshake(h) => &h.id, - _ => continue, - }; - let Some(result_tx) = sender_ack_map.write().await.remove(&id) else { - continue; - }; - // TODO learn how to handle other non-fatal websocket errors. - match e { - tungstenite::error::Error::Capacity(_) - | tungstenite::Error::Io(_) => { - let _ = result_tx.send(Err((*id, SendErrorKind::Timeout))); - } - _ => { - let _ = result_tx.send(Ok(NetworkMessage::Nack(*id))); - } - } - } - } - } - } - } => { - // println!("ws_sender died\r"); - }, - }; - return with; -} - -/// After a successful handshake, use information to spawn a new `peer_handler` task -/// and save a `Peer` in our peers mapping. Returns a sender to use for sending messages -/// to this peer, which will also be saved in its Peer struct. -pub fn create_new_peer( - our: Identity, - new_peer_id: Identity, - peers: Peers, - keys: PeerKeys, - secret: Arc>, - conn_sender: UnboundedSender<(NetworkMessage, Option)>, - kernel_message_tx: MessageSender, - net_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, -) -> Peer { - let (message_tx, message_rx) = unbounded_channel::<(PeerMessage, Option)>(); - let (decrypter_tx, decrypter_rx) = unbounded_channel::<(Vec, ErrorShuttle)>(); - let peer_id_name = new_peer_id.name.clone(); - let peer_conn_sender = conn_sender.clone(); - tokio::spawn(async move { - match peer_handler( - our, - peer_id_name.clone(), - secret, - message_rx, - decrypter_rx, - peer_conn_sender, - kernel_message_tx, - network_error_tx, - ) - .await - { - None => { - // println!("net: dropping peer handler but not deleting\r"); - } - Some(km) => { - // println!("net: ok actually deleting peer+keys now and retrying send\r"); - peers.write().await.remove(&peer_id_name); - keys.write().await.remove(&peer_id_name); - let _ = net_message_tx.send(km).await; - } - } - }); - return Peer { - identity: new_peer_id, - sender: message_tx, - decrypter: decrypter_tx, - socket_tx: conn_sender, - }; -} - -/// 1. take in messages from a specific peer, decrypt them, and send to kernel -/// 2. take in messages targeted at specific peer and either: -/// - encrypt them, and send to proper connection -/// - forward them untouched along the connection -async fn peer_handler( - our: Identity, - who: String, - secret: Arc>, - mut message_rx: UnboundedReceiver<(PeerMessage, Option)>, - mut decrypter_rx: UnboundedReceiver<(Vec, ErrorShuttle)>, - socket_tx: UnboundedSender<(NetworkMessage, Option)>, - kernel_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, -) -> Option { - // println!("peer_handler\r"); - let mut key = [0u8; 32]; - secret - .extract::(None) - .expand(&[], &mut key) - .unwrap(); - let cipher = XChaCha20Poly1305::new(generic_array::GenericArray::from_slice(&key)); - - let (ack_tx, mut ack_rx) = unbounded_channel::(); - // TODO use a more efficient data structure - let ack_map = Arc::new(RwLock::new(HashMap::::new())); - let recv_ack_map = ack_map.clone(); - tokio::select! { - // - // take in messages from a specific peer, decrypt them, and send to kernel - // - _ = async { - while let Some((encrypted_bytes, result_tx)) = decrypter_rx.recv().await { - let nonce = XNonce::from_slice(&encrypted_bytes[..24]); - if let Ok(decrypted) = cipher.decrypt(&nonce, &encrypted_bytes[24..]) { - if let Ok(message) = bincode::deserialize::(&decrypted) { - if message.source.node == who { - // println!("net: got peer message {}, acking\r", message.id); - let _ = result_tx.send(Ok(NetworkMessage::Ack(message.id))); - let _ = kernel_message_tx.send(message).await; - continue; - } - println!("net: got message 'from' wrong person! cheater/liar!\r"); - break; - } - println!("net: failed to deserialize message from {}\r", who); - break; - } - println!("net: failed to decrypt message from {}, could be spoofer\r", who); - break; - } - } => { - // println!("net: lost peer {who}\r"); - return None - } - // - // take in messages targeted at specific peer and either: - // - encrypt them, and send to proper connection - // - forward them untouched along the connection - // - _ = async { - // if we get a result_tx, rather than track it here, let a different - // part of the code handle whatever comes back from the socket. - while let Some((message, maybe_result_tx)) = message_rx.recv().await { - // if message is raw, we should encrypt. - // otherwise, simply send - match message { - PeerMessage::Raw(message) => { - let id = message.id; - if let Ok(bytes) = bincode::serialize::(&message) { - // generating a random nonce for each message. - // this isn't really as secure as we could get: should - // add a counter and then throw away the key when we hit a - // certain # of messages. TODO. - let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng); - if let Ok(encrypted) = cipher.encrypt(&nonce, bytes.as_ref()) { - if maybe_result_tx.is_none() { - ack_map.write().await.insert(id, message); - } - match socket_tx.send(( - NetworkMessage::Msg { - from: our.name.clone(), - to: who.clone(), - id: id, - contents: [nonce.to_vec(), encrypted].concat(), - }, - Some(maybe_result_tx.unwrap_or(ack_tx.clone())), - )) { - Ok(()) => tokio::task::yield_now().await, - Err(tokio::sync::mpsc::error::SendError((_, result_tx))) => { - // println!("net: lost socket with {who}\r"); - let _ = result_tx.unwrap().send(Ok(NetworkMessage::Nack(id))); - }, - } - } - } - } - PeerMessage::Net(net_message) => { - match socket_tx.send((net_message, Some(maybe_result_tx.unwrap_or(ack_tx.clone())))) { - Ok(()) => continue, - Err(tokio::sync::mpsc::error::SendError((net_message, result_tx))) => { - // println!("net: lost *forwarding* socket with {who}\r"); - let id = match net_message { - NetworkMessage::Msg { id, .. } => id, - NetworkMessage::Handshake(h) => h.id, - _ => continue, - }; - let _ = result_tx.unwrap().send(Ok(NetworkMessage::Nack(id))); - break; - }, - } - } - } - } - } => return None, - // - // receive acks and nacks from our socket - // throw away acks, but kill this peer and retry the send on nacks. - // - maybe_km = async { - while let Some(result) = ack_rx.recv().await { - match result { - Ok(NetworkMessage::Ack(id)) => { - // println!("net: got ack for message {}\r", id); - recv_ack_map.write().await.remove(&id); - continue; - } - Ok(NetworkMessage::Nack(id)) => { - // println!("net: got nack for message {}\r", id); - let Some(km) = recv_ack_map.write().await.remove(&id) else { - continue; - }; - // when we get a Nack, **delete this peer** and try to send the message again! - return Some(km) - } - Err((message_id, e)) => { - // println!("net: got error from ack_rx: {:?}\r", e); - // in practice this is always a timeout in current implementation - let Some(km) = recv_ack_map.write().await.remove(&message_id) else { - continue; - }; - let _ = network_error_tx - .send(WrappedSendError { - id: km.id, - source: km.source, - error: SendError { - kind: e, - target: km.target, - message: km.message, - payload: km.payload, - }, - }) - .await; - return None - } - _ => { - // println!("net: weird none ack\r"); - return None - } - } - } - return None; - } => { - // println!("net: exiting peer due to nackage\r"); - return maybe_km - }, - } -} diff --git a/src/net/mod.rs b/src/net/mod.rs index fefe7f7f..bcdb31ef 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,23 +1,27 @@ -use crate::net::connections::*; -use crate::net::types::*; +use crate::net::{types::*, utils::*}; use crate::types::*; -use anyhow::Result; -use elliptic_curve::ecdh::EphemeralSecret; -use elliptic_curve::PublicKey; -use ethers::prelude::k256::{self, Secp256k1}; -use ring::signature::{self, Ed25519KeyPair}; -use std::{collections::HashMap, sync::Arc}; +use anyhow::{anyhow, Result}; +use futures::{SinkExt, StreamExt}; +use rand::seq::SliceRandom; +use ring::signature::Ed25519KeyPair; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; use tokio::net::TcpListener; -use tokio::sync::{mpsc::unbounded_channel, RwLock}; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; use tokio::task::JoinSet; -use tokio::time::timeout; -use tokio_tungstenite::{accept_async, connect_async, MaybeTlsStream}; +use tokio::time; +use tokio_tungstenite::{accept_async, connect_async, MaybeTlsStream, WebSocketStream}; -mod connections; mod types; +mod utils; // only used in connection initialization, otherwise, nacks and Responses are only used for "timeouts" -const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(15); +const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); + +// 10 MB -- TODO analyze as desired, apps can always chunk data into many messages +const MESSAGE_MAX_SIZE: u32 = 10_485_800; /// Entry point from the main kernel task. Runs forever, spawns listener and sender tasks. pub async fn networking( @@ -27,676 +31,962 @@ pub async fn networking( kernel_message_tx: MessageSender, network_error_tx: NetworkErrorSender, print_tx: PrintSender, - message_tx: MessageSender, + self_message_tx: MessageSender, + message_rx: MessageReceiver, +) -> Result<()> { + println!("networking!\r"); + println!("our identity: {:#?}\r", our); + // branch on whether we are a direct or indirect node + match &our.ws_routing { + None => { + // indirect node: run the indirect networking strategy + indirect_networking( + our, + our_ip, + keypair, + kernel_message_tx, + network_error_tx, + print_tx, + self_message_tx, + message_rx, + ) + .await + } + Some((ip, port)) => { + // direct node: run the direct networking strategy + if &our_ip != ip { + return Err(anyhow!( + "net: fatal error: IP address mismatch: {} != {}, update your QNS identity", + our_ip, + ip + )); + } + let tcp = match TcpListener::bind(format!("0.0.0.0:{}", port)).await { + Ok(tcp) => tcp, + Err(_e) => { + return Err(anyhow!( + "net: fatal error: can't listen on port {}, update your QNS identity or free up that port", + port, + )); + } + }; + direct_networking( + our, + our_ip, + tcp, + keypair, + kernel_message_tx, + network_error_tx, + print_tx, + self_message_tx, + message_rx, + ) + .await + } + } +} + +async fn indirect_networking( + our: Identity, + our_ip: String, + keypair: Arc, + kernel_message_tx: MessageSender, + network_error_tx: NetworkErrorSender, + print_tx: PrintSender, + self_message_tx: MessageSender, mut message_rx: MessageReceiver, ) -> Result<()> { - // TODO persist this here - let pki: OnchainPKI = Arc::new(RwLock::new(HashMap::new())); - let keys: PeerKeys = Arc::new(RwLock::new(HashMap::new())); + println!("indirect_networking\r"); + let mut pki: OnchainPKI = HashMap::new(); + let mut peers: Peers = HashMap::new(); // mapping from QNS namehash to username - let names: PKINames = Arc::new(RwLock::new(HashMap::new())); + let mut names: PKINames = HashMap::new(); - // this only lives during a given run of the kernel - let peers: Peers = Arc::new(RwLock::new(HashMap::new())); + let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); + let mut active_routers = HashSet::::new(); - // listener task either kickstarts our networking by establishing active connections - // with one or more routers, or spawns a websocket listener if we are a direct node. - let listener = match &our.ws_routing { - None => { - // indirect node: connect to router(s) - tokio::spawn(connect_to_routers( - our.clone(), - keypair.clone(), - our_ip.clone(), - pki.clone(), - keys.clone(), - peers.clone(), - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - print_tx.clone(), - )) + // before opening up the main loop, go through our allowed routers + // and attempt to connect to all of them, saving the successfully + // connected-to ones in our router-set + connect_to_routers( + &our, + &our_ip, + &keypair, + &mut active_routers, + &pki, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone(), + ) + .await; + + loop { + tokio::select! { + // 1. receive messages from kernel and send out over connections, + // making new connections through our router-set as needed + Some(km) = message_rx.recv() => { + // got a message from kernel to send out over the network + let target = &km.target.node; + // if the message is for us, it's either a protocol-level "hello" message, + // or a debugging command issued from our terminal. handle it here: + if target == &our.name { + match handle_local_message( + &our, + &our_ip, + &keypair, + km, + &mut peers, + &mut pki, + &mut peer_connections, + None, + None, + Some(&active_routers), + &mut names, + &kernel_message_tx, + &print_tx, + ) + .await { + Ok(()) => {}, + Err(e) => { + print_tx.send(Printout { + verbosity: 0, + content: format!("net: error handling local message: {}", e) + }).await?; + } + } + } + // if the message is for a peer we currently have a connection with, + // try to send it to them + else if let Some(peer) = peers.get_mut(target) { + peer.sender.send(km)?; + } + else if let Some(peer_id) = pki.get(target) { + // if the message is for a *direct* peer we don't have a connection with, + // try to establish a connection with them + // TODO: here, we can *choose* to use our routers so as not to reveal + // networking information about ourselves to the target. + if peer_id.ws_routing.is_some() { + match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id.clone(), + routing_for: false, + sender: peer_tx, + }); + peers.insert(peer_name, peer.clone()); + peer.sender.send(km)?; + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Err(e) => { + println!("net: error initializing connection: {}\r", e); + error_offline(km, &network_error_tx).await?; + } + } + } + // if the message is for an *indirect* peer we don't have a connection with, + // do some routing: in a randomized order, go through their listed routers + // on chain and try to get one of them to build a proxied connection to + // this node for you + else { + let sent = time::timeout(TIMEOUT, + init_connection_via_router( + &our, + &our_ip, + &keypair, + km.clone(), + peer_id, + &pki, + &names, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone() + )).await; + if !sent.unwrap_or(false) { + // none of the routers worked! + println!("net: error initializing routed connection\r"); + error_offline(km, &network_error_tx).await?; + } + } + } + // peer cannot be found in PKI, throw an offline error + else { + error_offline(km, &network_error_tx).await?; + } + } + // 2. deal with active connections that die by removing the associated peer + // if the peer is one of our routers, remove them from router-set + Some(Ok((dead_peer, maybe_resend))) = peer_connections.join_next() => { + peers.remove(&dead_peer); + active_routers.remove(&dead_peer); + match maybe_resend { + None => {}, + Some(km) => { + self_message_tx.send(km).await?; + } + } + } + // 3. periodically attempt to connect to any allowed routers that we + // are not connected to + _ = time::sleep(time::Duration::from_secs(3)) => { + connect_to_routers( + &our, + &our_ip, + &keypair, + &mut active_routers, + &pki, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone(), + ) + .await; + } } - Some((_ip, port)) => { - // direct node: spawn the websocket listener - tokio::spawn(receive_incoming_connections( - our.clone(), - keypair.clone(), - *port, - pki.clone(), - keys.clone(), - peers.clone(), - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - )) + } +} + +async fn connect_to_routers( + our: &Identity, + our_ip: &str, + keypair: &Ed25519KeyPair, + active_routers: &mut HashSet, + pki: &OnchainPKI, + peers: &mut Peers, + peer_connections: &mut JoinSet<(NodeId, Option)>, + kernel_message_tx: MessageSender, +) { + for router in &our.allowed_routers { + if active_routers.contains(router) { + continue; + } + let Some(router_id) = pki.get(router) else { + continue; + }; + match init_connection(our, our_ip, router_id, keypair, None, true).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: router_id.clone(), + routing_for: false, + sender: peer_tx, + }); + println!("net: connected to router {}\r", peer_name); + peers.insert(peer_name.clone(), peer.clone()); + active_routers.insert(peer_name); + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Err(_e) => continue, + } + } +} + +async fn direct_networking( + our: Identity, + our_ip: String, + tcp: TcpListener, + keypair: Arc, + kernel_message_tx: MessageSender, + network_error_tx: NetworkErrorSender, + print_tx: PrintSender, + self_message_tx: MessageSender, + mut message_rx: MessageReceiver, +) -> Result<()> { + println!("direct_networking\r"); + let mut pki: OnchainPKI = HashMap::new(); + let mut peers: Peers = HashMap::new(); + // mapping from QNS namehash to username + let mut names: PKINames = HashMap::new(); + + let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); + let mut forwarding_connections = JoinSet::<()>::new(); + let mut pending_passthroughs: PendingPassthroughs = HashMap::new(); + + loop { + tokio::select! { + // 1. receive messages from kernel and send out over our connections, + // making new connections as needed + Some(km) = message_rx.recv() => { + // got a message from kernel to send out over the network + let target = &km.target.node; + // if the message is for us, it's either a protocol-level "hello" message, + // or a debugging command issued from our terminal. handle it here: + if target == &our.name { + match handle_local_message( + &our, + &our_ip, + &keypair, + km, + &mut peers, + &mut pki, + &mut peer_connections, + Some(&mut pending_passthroughs), + Some(&forwarding_connections), + None, + &mut names, + &kernel_message_tx, + &print_tx, + ) + .await { + Ok(()) => {}, + Err(e) => { + print_tx.send(Printout { + verbosity: 0, + content: format!("net: error handling local message: {}", e) + }).await?; + } + } + } + // if the message is for a peer we currently have a connection with, + // try to send it to them + else if let Some(peer) = peers.get_mut(target) { + peer.sender.send(km)?; + } + else if let Some(peer_id) = pki.get(target) { + // if the message is for a *direct* peer we don't have a connection with, + // try to establish a connection with them + if peer_id.ws_routing.is_some() { + match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id.clone(), + routing_for: false, + sender: peer_tx, + }); + peers.insert(peer_name, peer.clone()); + peer.sender.send(km)?; + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Err(e) => { + println!("net: error initializing connection: {}\r", e); + error_offline(km, &network_error_tx).await?; + } + } + } + // if the message is for an *indirect* peer we don't have a connection with, + // do some routing: in a randomized order, go through their listed routers + // on chain and try to get one of them to build a proxied connection to + // this node for you + else { + let sent = time::timeout(TIMEOUT, + init_connection_via_router( + &our, + &our_ip, + &keypair, + km.clone(), + peer_id, + &pki, + &names, + &mut peers, + &mut peer_connections, + kernel_message_tx.clone() + )).await; + if !sent.unwrap_or(false) { + // none of the routers worked! + println!("net: error initializing routed connection\r"); + error_offline(km, &network_error_tx).await?; + } + } + } + // peer cannot be found in PKI, throw an offline error + else { + error_offline(km, &network_error_tx).await?; + } + } + // 2. receive incoming TCP connections + Ok((stream, _socket_addr)) = tcp.accept() => { + // TODO we can perform some amount of validation here + // to prevent some amount of potential DDoS attacks. + // can also block based on socket_addr + match accept_async(MaybeTlsStream::Plain(stream)).await { + Ok(websocket) => { + let (peer_id, routing_for, conn) = + match recv_connection( + &our, + &our_ip, + &pki, + &peers, + &mut pending_passthroughs, + &keypair, + websocket).await + { + Ok(res) => res, + Err(e) => { + println!("net: recv_connection failed: {e}\r"); + continue; + } + }; + // if conn is direct, add peer + // if passthrough, add to our forwarding connections joinset + match conn { + Connection::Peer(peer_conn) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id, + routing_for, + sender: peer_tx, + }); + peers.insert(peer.identity.name.clone(), peer.clone()); + peer_connections.spawn(maintain_connection( + peer, + peer_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } + Connection::Passthrough(passthrough_conn) => { + forwarding_connections.spawn(maintain_passthrough( + passthrough_conn, + )); + } + Connection::PendingPassthrough(pending_conn) => { + pending_passthroughs.insert( + (peer_id.name.clone(), pending_conn.target.clone()), + pending_conn + ); + } + } + } + // ignore connections we failed to accept...? + Err(_) => {} + } + } + // 3. deal with active connections that die by removing the associated peer + Some(Ok((dead_peer, maybe_resend))) = peer_connections.join_next() => { + peers.remove(&dead_peer); + match maybe_resend { + None => {}, + Some(km) => { + self_message_tx.send(km).await?; + } + } + } + } + } +} + +async fn init_connection_via_router( + our: &Identity, + our_ip: &str, + keypair: &Ed25519KeyPair, + km: KernelMessage, + peer_id: &Identity, + pki: &OnchainPKI, + names: &PKINames, + peers: &mut Peers, + peer_connections: &mut JoinSet<(NodeId, Option)>, + kernel_message_tx: MessageSender, +) -> bool { + println!("init_connection_via_router\r"); + let routers_shuffled = { + let mut routers = peer_id.allowed_routers.clone(); + routers.shuffle(&mut rand::thread_rng()); + routers + }; + for router_namehash in &routers_shuffled { + let Some(router_name) = names.get(router_namehash) else { + continue; + }; + let router_id = match pki.get(router_name) { + None => continue, + Some(id) => id, + }; + match init_connection(&our, &our_ip, peer_id, &keypair, Some(router_id), false).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id.clone(), + routing_for: false, + sender: peer_tx, + }); + peers.insert(peer_name, peer.clone()); + peer.sender.send(km).unwrap(); + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + )); + return true; + } + Err(_) => continue, + } + } + return false; +} + +async fn maintain_connection( + peer: Arc, + mut conn: PeerConnection, + mut peer_rx: UnboundedReceiver, + kernel_message_tx: MessageSender, + // network_error_tx: NetworkErrorSender, + // print_tx: PrintSender, +) -> (NodeId, Option) { + println!("maintain_connection\r"); + loop { + tokio::select! { + recv_result = recv_uqbar_message(&mut conn) => { + match recv_result { + Ok(km) => { + if km.source.node != peer.identity.name { + println!("net: got message with spoofed source\r"); + return (peer.identity.name.clone(), None) + } + kernel_message_tx.send(km).await.expect("net error: fatal: kernel died"); + } + Err(e) => { + println!("net: error receiving message: {}\r", e); + return (peer.identity.name.clone(), None) + } + } + }, + maybe_recv = peer_rx.recv() => { + match maybe_recv { + Some(km) => { + // TODO error handle + match send_uqbar_message(&km, &mut conn).await { + Ok(()) => continue, + Err(e) => { + println!("net: error sending message: {}\r", e); + return (peer.identity.name.clone(), Some(km)) + } + } + } + None => { + println!("net: peer disconnected\r"); + return (peer.identity.name.clone(), None) + } + } + }, + } + } +} + +/// match the streams +/// TODO optimize performance of this +async fn maintain_passthrough(mut conn: PassthroughConnection) { + println!("maintain_passthrough\r"); + loop { + tokio::select! { + maybe_recv = conn.read_stream_1.next() => { + match maybe_recv { + Some(Ok(msg)) => { + conn.write_stream_2.send(msg).await.expect("net error: fatal: kernel died"); + } + _ => { + println!("net: passthrough broke\r"); + return + } + } + }, + maybe_recv = conn.read_stream_2.next() => { + match maybe_recv { + Some(Ok(msg)) => { + conn.write_stream_1.send(msg).await.expect("net error: fatal: kernel died"); + } + _ => { + println!("net: passthrough broke\r"); + return + } + } + }, + } + } +} + +async fn recv_connection( + our: &Identity, + our_ip: &str, + pki: &OnchainPKI, + peers: &Peers, + pending_passthroughs: &mut PendingPassthroughs, + keypair: &Ed25519KeyPair, + websocket: WebSocketStream>, +) -> Result<(Identity, bool, Connection)> { + println!("recv_connection\r"); + let mut buf = vec![0u8; 65535]; + let (mut noise, our_static_key) = build_responder(); + let (mut write_stream, mut read_stream) = websocket.split(); + + // before we begin XX handshake pattern, check first message over socket + let first_message = &ws_recv(&mut read_stream).await?; + + // if the first message contains a "routing request", + // we see if the target is someone we are actively routing for, + // and create a Passthrough connection if so. + // a Noise 'e' message with have len 32 + if first_message.len() != 32 { + let (their_id, target_name) = validate_routing_request(&our.name, &first_message, pki)?; + let (id, conn) = create_passthrough( + our, + our_ip, + their_id, + target_name, + pki, + peers, + pending_passthroughs, + write_stream, + read_stream, + ) + .await?; + return Ok((id, false, conn)); + } + + // <- e + noise.read_message(first_message, &mut buf)?; + + // -> e, ee, s, es + send_uqbar_handshake( + &our, + keypair, + &our_static_key, + &mut noise, + &mut buf, + &mut write_stream, + false, + ) + .await?; + + // <- s, se + let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; + + // now validate this handshake payload against the QNS PKI + let their_id = pki + .get(&their_handshake.name) + .ok_or(anyhow!("unknown QNS name"))?; + validate_handshake( + &their_handshake, + noise + .get_remote_static() + .ok_or(anyhow!("noise error: missing remote pubkey"))?, + their_id, + )?; + + // Transition the state machine into transport mode now that the handshake is complete. + let noise = noise.into_transport_mode()?; + println!("handshake complete, noise session received\r"); + + // TODO if their handshake indicates they want us to proxy + // for them (aka act as a router for them) we can choose + // whether to do so here. + Ok(( + their_id.clone(), + their_handshake.proxy_request, + Connection::Peer(PeerConnection { + noise, + buf, + write_stream, + read_stream, + }), + )) +} + +async fn recv_connection_via_router( + our: &Identity, + our_ip: &str, + their_name: &str, + pki: &OnchainPKI, + keypair: &Ed25519KeyPair, + router: &Identity, +) -> Result<(Identity, PeerConnection)> { + println!("recv_connection_via_router\r"); + let mut buf = vec![0u8; 65535]; + let (mut noise, our_static_key) = build_responder(); + + let Some((ref ip, ref port)) = router.ws_routing else { + return Err(anyhow!("router has no routing information")); + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + else { + return Err(anyhow!("failed to connect to target")); + }; + let (mut write_stream, mut read_stream) = websocket.split(); + + // before beginning XX handshake pattern, send a routing request + let message = bincode::serialize(&RoutingRequest { + source: our.name.clone(), + signature: keypair + .sign([their_name, router.name.as_str()].concat().as_bytes()) + .as_ref() + .to_vec(), + target: their_name.to_string(), + protocol_version: 1, + })?; + ws_send(&mut write_stream, &message).await?; + + // <- e + noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; + + // -> e, ee, s, es + send_uqbar_handshake( + &our, + keypair, + &our_static_key, + &mut noise, + &mut buf, + &mut write_stream, + false, + ) + .await?; + + // <- s, se + let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; + + // now validate this handshake payload against the QNS PKI + let their_id = pki + .get(&their_handshake.name) + .ok_or(anyhow!("unknown QNS name"))?; + validate_handshake( + &their_handshake, + noise + .get_remote_static() + .ok_or(anyhow!("noise error: missing remote pubkey"))?, + their_id, + )?; + + // Transition the state machine into transport mode now that the handshake is complete. + let noise = noise.into_transport_mode()?; + println!("handshake complete, noise session received\r"); + + Ok(( + their_id.clone(), + PeerConnection { + noise, + buf, + write_stream, + read_stream, + }, + )) +} + +async fn init_connection( + our: &Identity, + our_ip: &str, + peer_id: &Identity, + keypair: &Ed25519KeyPair, + use_router: Option<&Identity>, + proxy_request: bool, +) -> Result<(String, PeerConnection)> { + println!("init_connection\r"); + let mut buf = vec![0u8; 65535]; + let (mut noise, our_static_key) = build_initiator(); + + let (mut write_stream, mut read_stream) = match use_router { + None => { + let Some((ref ip, ref port)) = peer_id.ws_routing else { + return Err(anyhow!("target has no routing information")); + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + else { + return Err(anyhow!("failed to connect to target")); + }; + websocket.split() + } + Some(router_id) => { + let Some((ref ip, ref port)) = router_id.ws_routing else { + return Err(anyhow!("router has no routing information")); + }; + let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { + return Err(anyhow!("failed to parse websocket url")); + }; + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + else { + return Err(anyhow!("failed to connect to target")); + }; + websocket.split() } }; - let _ = tokio::join!(listener, async { - while let Some(km) = message_rx.recv().await { - // got a message from kernel to send out over the network - let target = &km.target.node; - // if the message is for us, it's either a protocol-level "hello" message, - // or a debugging command issued from our terminal. handle it here: - if target == &our.name { - handle_incoming_message( - &our, - km, - peers.clone(), - keys.clone(), - pki.clone(), - names.clone(), - kernel_message_tx.clone(), - print_tx.clone(), + // if this is a routed request, before starting XX handshake pattern, send a + // routing request message over socket + if use_router.is_some() { + let message = bincode::serialize(&RoutingRequest { + source: our.name.clone(), + signature: keypair + .sign( + [&peer_id.name, use_router.unwrap().name.as_str()] + .concat() + .as_bytes(), ) - .await; - continue; - } - let peers_read = peers.read().await; - // - // we have the target as an active peer, meaning we can send the message directly - // - if let Some(peer) = peers_read.get(target) { - // println!("net: direct send to known peer\r"); - match peer.sender.send((PeerMessage::Raw(km.clone()), None)) { - Ok(_) => continue, - Err(_) => { - // println!("net: failed to send to known peer\r"); - drop(peers_read); - peers.write().await.remove(target); - error_offline(km, &network_error_tx).await; - continue; - } - } - } - drop(peers_read); - // - // we don't have the target as a peer yet, but we have shaken hands with them - // before, and can try to reuse that shared secret to send a message. - // first, we'll need to open a websocket and create a Peer struct for them. - // - if let Some((peer_id, secret)) = keys.read().await.get(target).cloned() { - // - // we can establish a connection directly with this peer - // - if let Some((ref ip, ref port)) = peer_id.ws_routing { - // println!("net: creating direct connection to peer with known keys\r"); - let Ok(ws_url) = make_ws_url(&our_ip, ip, port) else { - error_offline(km, &network_error_tx).await; - continue; - }; - let Ok(Ok((websocket, _response))) = - timeout(TIMEOUT, connect_async(ws_url)).await - else { - error_offline(km, &network_error_tx).await; - continue; - }; - let (socket_tx, conn_handle) = build_connection( - our.clone(), - keypair.clone(), - pki.clone(), - keys.clone(), - peers.clone(), - websocket, - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - None, - ) - .await; - let new_peer = create_new_peer( - our.clone(), - peer_id.clone(), - peers.clone(), - keys.clone(), - secret, - socket_tx.clone(), - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - ); - let (temp_result_tx, mut temp_result_rx) = unbounded_channel::(); - let _ = new_peer - .sender - .send((PeerMessage::Raw(km.clone()), Some(temp_result_tx))); - match timeout(TIMEOUT, temp_result_rx.recv()).await { - Ok(Some(Ok(NetworkMessage::Ack(_id)))) => { - peers.write().await.insert(peer_id.name.clone(), new_peer); - continue; - } - _ => { - // instead of throwing Offline now, throw away their keys and - // try again. - keys.write().await.remove(target); - conn_handle.abort(); - } - } - } - // - // need to find a router that will connect to this peer! - // - else { - // println!("net: finding router for peer with known keys\r"); - // get their ID from PKI so we have their most up-to-date router list - let Some(peer_id) = pki.read().await.get(target).cloned() else { - // this target cannot be found in the PKI! - // throw an Offline error. - error_offline(km, &network_error_tx).await; - continue; - }; - let mut success = false; - for router_namehash in &peer_id.allowed_routers { - let km = km.clone(); - let Some(router_name) = names.read().await.get(router_namehash).cloned() - else { - continue; - }; - let Some(router_id) = pki.read().await.get(&router_name).cloned() else { - continue; - }; - let Some((ref ip, ref port)) = router_id.ws_routing else { - continue; - }; - // - // otherwise, attempt to connect to the router's IP+port and send through that - // - // if we already have this router as a peer, use that socket_tx - let (socket_tx, maybe_conn_handle) = - if let Some(router) = peers.read().await.get(&router_name) { - (router.socket_tx.clone(), None) - } else { - let Ok(ws_url) = make_ws_url(&our_ip, ip, port) else { - continue; - }; - let Ok(Ok((websocket, _response))) = - timeout(TIMEOUT, connect_async(ws_url)).await - else { - continue; - }; - let (socket_tx, conn_handle) = build_connection( - our.clone(), - keypair.clone(), - pki.clone(), - keys.clone(), - peers.clone(), - websocket, - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - None, - ) - .await; - (socket_tx, Some(conn_handle)) - }; - let new_peer = create_new_peer( - our.clone(), - peer_id.clone(), - peers.clone(), - keys.clone(), - secret.clone(), - socket_tx.clone(), - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - ); - let (temp_result_tx, mut temp_result_rx) = - unbounded_channel::(); - let _ = new_peer - .sender - .send((PeerMessage::Raw(km.clone()), Some(temp_result_tx))); - match timeout(TIMEOUT, temp_result_rx.recv()).await { - Ok(Some(Ok(NetworkMessage::Ack(_id)))) => { - peers.write().await.insert(peer_id.name.clone(), new_peer); - success = true; - break; - } - _ => { - if let Some(conn_handle) = maybe_conn_handle { - conn_handle.abort(); - } - continue; - } - } - } - if !success { - // instead of throwing Offline now, throw away their keys and - // try again. - keys.write().await.remove(target); - } - } - } - // - // sending a message to a peer for which we don't have active networking info. - // this means that we need to search the PKI for the peer, and then attempt to - // exchange handshakes with them. - // - let Some(peer_id) = pki.read().await.get(target).cloned() else { - // this target cannot be found in the PKI! - // throw an Offline error. - error_offline(km, &network_error_tx).await; - continue; - }; - // - // we can establish a connection directly with this peer, then send a handshake - // - if let Some((ref ip, ref port)) = peer_id.ws_routing { - // println!("net: creating direct connection to peer in PKI\r"); - let Ok(ws_url) = make_ws_url(&our_ip, ip, port) else { - error_offline(km, &network_error_tx).await; - continue; - }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await - else { - error_offline(km, &network_error_tx).await; - continue; - }; - let (socket_tx, conn_handle) = build_connection( - our.clone(), - keypair.clone(), - pki.clone(), - keys.clone(), - peers.clone(), - websocket, - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - None, - ) - .await; - let (secret, handshake) = - make_secret_and_handshake(&our, keypair.clone(), target, None); - let (handshake_tx, mut handshake_rx) = unbounded_channel::(); - socket_tx - .send((NetworkMessage::Handshake(handshake), Some(handshake_tx))) - .unwrap(); - let response_shake = match timeout(TIMEOUT, handshake_rx.recv()).await { - Ok(Some(Ok(NetworkMessage::HandshakeAck(shake)))) => shake, - _ => { - // println!("net: failed handshake with {target}\r"); - error_offline(km, &network_error_tx).await; - conn_handle.abort(); - continue; - } - }; - let Ok(their_ephemeral_pk) = validate_handshake(&response_shake, &peer_id) else { - // println!("net: failed handshake with {target}\r"); - error_offline(km, &network_error_tx).await; - conn_handle.abort(); - continue; - }; - let secret = Arc::new(secret.diffie_hellman(&their_ephemeral_pk)); - // save the handshake to our Keys map - keys.write() - .await - .insert(peer_id.name.clone(), (peer_id.clone(), secret.clone())); - let new_peer = create_new_peer( - our.clone(), - peer_id.clone(), - peers.clone(), - keys.clone(), - secret, - socket_tx.clone(), - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - ); - // can't do a self_tx.send here because we need to maintain ordering of messages - // already queued. - let (temp_result_tx, mut temp_result_rx) = unbounded_channel::(); - let _ = new_peer - .sender - .send((PeerMessage::Raw(km.clone()), Some(temp_result_tx))); - match timeout(TIMEOUT, temp_result_rx.recv()).await { - Ok(Some(Ok(NetworkMessage::Ack(_id)))) => { - peers.write().await.insert(peer_id.name.clone(), new_peer); - continue; - } - _ => { - // instead of throwing Offline now, throw away their keys and - // try again. - keys.write().await.remove(target); - conn_handle.abort(); - } - } - } - // - // need to find a router that will connect to this peer, then do a handshake - // - else { - // println!("net: looking for router to create connection to peer in PKI\r"); - let Some(peer_id) = pki.read().await.get(target).cloned() else { - // this target cannot be found in the PKI! - // throw an Offline error. - error_offline(km, &network_error_tx).await; - continue; - }; - let mut success = false; - for router_namehash in &peer_id.allowed_routers { - let km = km.clone(); - let Some(router_name) = names.read().await.get(router_namehash).cloned() else { - continue; - }; - if router_name == our.name { - // don't try to connect to ourselves! - continue; - } - let Some(router_id) = pki.read().await.get(&router_name).cloned() else { - continue; - }; - let Some((ref ip, ref port)) = router_id.ws_routing else { - continue; - }; - // - // attempt to connect to the router's IP+port and send through that - // if we already have this router as a peer, use that socket_tx - let (socket_tx, maybe_conn_handle) = - if let Some(router) = peers.read().await.get(&router_name) { - (router.socket_tx.clone(), None) - } else { - let Ok(ws_url) = make_ws_url(&our_ip, ip, port) else { - continue; - }; - let Ok(Ok((websocket, _response))) = - timeout(TIMEOUT, connect_async(ws_url)).await - else { - continue; - }; - let (socket_tx, conn_handle) = build_connection( - our.clone(), - keypair.clone(), - pki.clone(), - keys.clone(), - peers.clone(), - websocket, - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - None, - ) - .await; - (socket_tx, Some(conn_handle)) - }; - let (secret, handshake) = - make_secret_and_handshake(&our, keypair.clone(), target, None); - let (handshake_tx, mut handshake_rx) = unbounded_channel::(); - socket_tx - .send((NetworkMessage::Handshake(handshake), Some(handshake_tx))) - .unwrap(); - let response_shake = match timeout(TIMEOUT, handshake_rx.recv()).await { - Ok(Some(Ok(NetworkMessage::HandshakeAck(shake)))) => shake, - _ => { - if let Some(conn_handle) = maybe_conn_handle { - conn_handle.abort(); - } - continue; - } - }; - let Ok(their_ephemeral_pk) = validate_handshake(&response_shake, &peer_id) - else { - if let Some(conn_handle) = maybe_conn_handle { - conn_handle.abort(); - } - continue; - }; - let secret = Arc::new(secret.diffie_hellman(&their_ephemeral_pk)); - // save the handshake to our Keys map - keys.write() - .await - .insert(peer_id.name.clone(), (peer_id.clone(), secret.clone())); - let new_peer = create_new_peer( - our.clone(), - peer_id.clone(), - peers.clone(), - keys.clone(), - secret, - socket_tx.clone(), - kernel_message_tx.clone(), - message_tx.clone(), - network_error_tx.clone(), - ); - let (temp_result_tx, mut temp_result_rx) = unbounded_channel::(); - let _ = new_peer - .sender - .send((PeerMessage::Raw(km.clone()), Some(temp_result_tx))); - match timeout(TIMEOUT, temp_result_rx.recv()).await { - Ok(Some(Ok(NetworkMessage::Ack(_id)))) => { - peers.write().await.insert(peer_id.name.clone(), new_peer); - success = true; - break; - } - _ => { - if let Some(conn_handle) = maybe_conn_handle { - conn_handle.abort(); - } - continue; - } - } - } - if !success { - error_offline(km, &network_error_tx).await; - continue; - } - } - } - }); - Err(anyhow::anyhow!("networking task exited")) -} - -async fn error_offline(km: KernelMessage, network_error_tx: &NetworkErrorSender) { - let _ = network_error_tx - .send(WrappedSendError { - id: km.id, - source: km.source, - error: SendError { - kind: SendErrorKind::Offline, - target: km.target, - message: km.message, - payload: km.payload, - }, - }) - .await; -} - -/// Only used if an indirect node. -async fn connect_to_routers( - our: Identity, - keypair: Arc, - our_ip: String, - pki: OnchainPKI, - keys: PeerKeys, - peers: Peers, - kernel_message_tx: MessageSender, - net_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, - print_tx: PrintSender, -) { - // as soon as we boot, need to try and connect to all of our allowed_routers - // we can "throw away" routers that have a bad URL setup - // - // any time we *lose* a router, we need to try and reconnect on a loop - // we should always be trying to connect to all "good" routers we don't already have - - let (routers_to_try_tx, mut routers_to_try_rx) = unbounded_channel::(); - let mut active_routers = JoinSet::, tokio::task::JoinError>>::new(); - - // at start, add all our routers to list - for router_name in our.allowed_routers.clone() { - routers_to_try_tx.send(router_name).unwrap(); + .as_ref() + .to_vec(), + target: peer_id.name.clone(), + protocol_version: 1, + })?; + ws_send(&mut write_stream, &message).await?; } - loop { - // we sleep here in order not to BLAST routers with connections - // if we have a PKI mismatch with them for some amount of time. - tokio::time::sleep(std::time::Duration::from_secs(2)).await; - tokio::select! { - Some(Ok(Ok(Some(dead_router)))) = active_routers.join_next() => { - let _ = print_tx - .send(Printout { - verbosity: 0, - content: format!("net: connection to router {dead_router} died"), - }) - .await; - peers.write().await.remove(&dead_router); - if active_routers.is_empty() { - let _ = print_tx - .send(Printout { - verbosity: 0, - content: format!("net: no working routers, we are offline!"), - }) - .await; - } - let _ = routers_to_try_tx.send(dead_router); - } - Some(router_name) = routers_to_try_rx.recv() => { - if peers.read().await.contains_key(&router_name) { - continue; - } - let Some(router_id) = pki.read().await.get(&router_name).cloned() else { - let _ = routers_to_try_tx.send(router_name); - continue; - }; - let Some((ref ip, ref port)) = router_id.ws_routing else { - // this is a bad router, can remove from our list - continue; - }; - let Ok(ws_url) = make_ws_url(&our_ip, ip, port) else { - // this is a bad router, can remove from our list - continue; - }; - let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { - let _ = routers_to_try_tx.send(router_name); - continue; - }; - // we never try to reuse keys with routers because we need to make - // sure we have a live connection with them. - // connect to their websocket and then send a handshake. - // save the handshake in our keys map, then save them as an active peer. - let (socket_tx, conn_handle) = build_connection( - our.clone(), - keypair.clone(), - pki.clone(), - keys.clone(), - peers.clone(), - websocket, - kernel_message_tx.clone(), - net_message_tx.clone(), - network_error_tx.clone(), - Some(router_name.clone()), - ) - .await; - let (secret, handshake) = - make_secret_and_handshake(&our, keypair.clone(), &router_name, None); - let (handshake_tx, mut handshake_rx) = unbounded_channel::(); - socket_tx - .send((NetworkMessage::Handshake(handshake), Some(handshake_tx))) - .unwrap(); - let response_shake = match timeout(TIMEOUT, handshake_rx.recv()).await { - Ok(Some(Ok(NetworkMessage::HandshakeAck(shake)))) => shake, - _ => { - // println!("net: failed handshake with {router_name}\r"); - conn_handle.abort(); - let _ = routers_to_try_tx.send(router_name); - continue; - } - }; - let Ok(their_ephemeral_pk) = validate_handshake(&response_shake, &router_id) else { - // println!("net: failed handshake with {router_name}\r"); - conn_handle.abort(); - let _ = routers_to_try_tx.send(router_name); - continue; - }; - let secret = Arc::new(secret.diffie_hellman(&their_ephemeral_pk)); - // save the handshake to our Keys map - keys.write().await.insert( - router_id.name.clone(), - (router_id.clone(), secret.clone()), - ); - let new_peer = create_new_peer( - our.clone(), - router_id.clone(), - peers.clone(), - keys.clone(), - secret, - socket_tx.clone(), - kernel_message_tx.clone(), - net_message_tx.clone(), - network_error_tx.clone(), - ); - let _ = print_tx - .send(Printout { - verbosity: 0, - content: format!("net: connected to router {router_name}"), - }) - .await; - peers.write().await.insert(router_id.name.clone(), new_peer); - active_routers.spawn(conn_handle); - } - } - } + // -> e + let len = noise.write_message(&[], &mut buf)?; + ws_send(&mut write_stream, &buf[..len]).await?; + + // <- e, ee, s, es + let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; + + // now validate this handshake payload against the QNS PKI + validate_handshake( + &their_handshake, + noise + .get_remote_static() + .ok_or(anyhow!("noise error: missing remote pubkey"))?, + peer_id, + )?; + + // -> s, se + send_uqbar_handshake( + &our, + keypair, + &our_static_key, + &mut noise, + &mut buf, + &mut write_stream, + proxy_request, + ) + .await?; + + let noise = noise.into_transport_mode()?; + println!("handshake complete, noise session initiated\r"); + + Ok(( + their_handshake.name, + PeerConnection { + noise, + buf, + write_stream, + read_stream, + }, + )) } -/// only used if direct. should live forever -async fn receive_incoming_connections( - our: Identity, - keypair: Arc, - port: u16, - pki: OnchainPKI, - keys: PeerKeys, - peers: Peers, - kernel_message_tx: MessageSender, - net_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, -) { - let tcp = TcpListener::bind(format!("0.0.0.0:{}", port)) - .await - .expect(format!("net: fatal error: can't listen on port {port}. change port onchain or free up this port!").as_str()); - - while let Ok((stream, _socket_addr)) = tcp.accept().await { - // TODO we can perform some amount of validation here - // to prevent some amount of potential DDoS attacks. - // can block based on socket_addr, but not QNS ID. - match accept_async(MaybeTlsStream::Plain(stream)).await { - Ok(websocket) => { - // println!("received incoming connection\r"); - tokio::spawn(build_connection( - our.clone(), - keypair.clone(), - pki.clone(), - keys.clone(), - peers.clone(), - websocket, - kernel_message_tx.clone(), - net_message_tx.clone(), - network_error_tx.clone(), - None, - )); - } - // ignore connections we failed to accept - Err(_) => {} - } - } -} - -/// net module only handles requests, will never return a response -async fn handle_incoming_message( +/// net module only handles incoming local requests, will never return a response +async fn handle_local_message( our: &Identity, + our_ip: &str, + keypair: &Ed25519KeyPair, km: KernelMessage, - peers: Peers, - keys: PeerKeys, - pki: OnchainPKI, - names: PKINames, - kernel_message_tx: MessageSender, - print_tx: PrintSender, -) { + peers: &mut Peers, + pki: &mut OnchainPKI, + peer_connections: &mut JoinSet<(NodeId, Option)>, + pending_passthroughs: Option<&mut PendingPassthroughs>, + forwarding_connections: Option<&JoinSet<()>>, + active_routers: Option<&HashSet>, + names: &mut PKINames, + kernel_message_tx: &MessageSender, + print_tx: &PrintSender, +) -> Result<()> { + println!("handle_local_message\r"); let ipc = match km.message { - Message::Response(_) => return, Message::Request(request) => request.ipc, + Message::Response((response, _context)) => { + // these are received as a router, when we send ConnectionRequests + // to a node we do routing for. + match serde_json::from_slice::(&response.ipc)? { + NetResponses::Attempting(_) => { + // TODO anything here? + } + NetResponses::Rejected(to) => { + // drop from our pending map + // this will drop the socket, causing initiator to see it as failed + pending_passthroughs + .ok_or(anyhow!("got net response as non-router"))? + .remove(&(to, km.source.node)); + } + } + return Ok(()); + } }; if km.source.node != our.name { + if let Ok(act) = serde_json::from_slice::(&ipc) { + match act { + NetActions::QnsBatchUpdate(_) | NetActions::QnsUpdate(_) => { + // for now, we don't get these from remote. + } + NetActions::ConnectionRequest(from) => { + // someone wants to open a passthrough with us through a router! + // if we are an indirect node, and source is one of our routers, + // respond by attempting to init a matching passthrough. + // TODO can discriminate more here.. + if our.allowed_routers.contains(&km.source.node) { + let Ok((peer_id, peer_conn)) = time::timeout(TIMEOUT, + recv_connection_via_router( + our, + our_ip, + &from, + pki, + keypair, + &peers + .get(&km.source.node) + .ok_or(anyhow!("unknown router"))? + .identity, + )).await? else { + return Err(anyhow!("someone tried to connect to us but it timed out")) + }; + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: peer_id, + routing_for: false, + sender: peer_tx, + }); + peers.insert(peer.identity.name.clone(), peer.clone()); + peer_connections.spawn(maintain_connection( + peer, + peer_conn, + peer_rx, + kernel_message_tx.clone(), + )); + } else { + kernel_message_tx + .send(KernelMessage { + id: km.id, + source: Address { + node: our.name.clone(), + process: ProcessId::from_str("net:sys:uqbar").unwrap(), + }, + target: km.rsvp.unwrap_or(km.source), + rsvp: None, + message: Message::Response(( + Response { + inherit: false, + ipc: serde_json::to_vec(&NetResponses::Rejected(from))?, + metadata: None, + }, + None, + )), + payload: None, + signed_capabilities: None, + }) + .await?; + } + } + } + return Ok(()); + }; + // if we can't parse this to a netaction, treat it as a hello and print it // respond to a text message with a simple "delivered" response - let _ = print_tx + print_tx .send(Printout { verbosity: 0, content: format!( @@ -705,8 +995,8 @@ async fn handle_incoming_message( std::str::from_utf8(&ipc).unwrap_or("!!message parse error!!") ), }) - .await; - let _ = kernel_message_tx + .await?; + kernel_message_tx .send(KernelMessage { id: km.id, source: Address { @@ -726,67 +1016,121 @@ async fn handle_incoming_message( payload: None, signed_capabilities: None, }) - .await; + .await?; + Ok(()) } else { - // available commands: "peers", "QnsUpdate" (see qns_indexer module) + // available commands: "peers", "pki", "names", "diagnostics" // first parse as raw string, then deserialize to NetActions object match std::str::from_utf8(&ipc) { Ok("peers") => { - let peer_read = peers.read().await; - let _ = print_tx + print_tx .send(Printout { verbosity: 0, - content: format!("{:?}", peer_read.keys()), + content: format!("{:#?}", peers.keys()), }) - .await; - } - Ok("keys") => { - let keys_read = keys.read().await; - let _ = print_tx - .send(Printout { - verbosity: 0, - content: format!("{:?}", keys_read.keys()), - }) - .await; + .await?; } Ok("pki") => { - let pki_read = pki.read().await; - let _ = print_tx + print_tx .send(Printout { verbosity: 0, - content: format!("{:?}", pki_read), + content: format!("{:#?}", pki), }) - .await; + .await?; } Ok("names") => { - let names_read = names.read().await; - let _ = print_tx + print_tx .send(Printout { verbosity: 0, - content: format!("{:?}", names_read), + content: format!("{:#?}", names), }) - .await; + .await?; + } + Ok("diagnostics") => { + print_tx + .send(Printout { + verbosity: 0, + content: format!("our Identity: {:#?}", our), + }) + .await?; + print_tx + .send(Printout { + verbosity: 0, + content: format!("we have connections with peers: {:#?}", peers.keys()), + }) + .await?; + print_tx + .send(Printout { + verbosity: 0, + content: format!("we have {} entries in the PKI", pki.len()), + }) + .await?; + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} open peer connections", + peer_connections.len() + ), + }) + .await?; + if pending_passthroughs.is_some() { + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} pending passthrough connections", + pending_passthroughs.unwrap().len() + ), + }) + .await?; + } + if forwarding_connections.is_some() { + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} open passthrough connections", + forwarding_connections.unwrap().len() + ), + }) + .await?; + } + if active_routers.is_some() { + print_tx + .send(Printout { + verbosity: 0, + content: format!( + "we have {} active routers", + active_routers.unwrap().len() + ), + }) + .await?; + } } _ => { let Ok(act) = serde_json::from_slice::(&ipc) else { - let _ = print_tx + print_tx .send(Printout { verbosity: 0, content: "net: got unknown command".into(), }) - .await; - return; + .await?; + return Ok(()); }; match act { + NetActions::ConnectionRequest(_) => { + // we shouldn't receive these from ourselves. + } NetActions::QnsUpdate(log) => { - let _ = print_tx + print_tx .send(Printout { verbosity: 1, content: format!("net: got QNS update for {}", log.name), }) - .await; + .await?; - let _ = pki.write().await.insert( + pki.insert( log.name.clone(), Identity { name: log.name.clone(), @@ -799,10 +1143,10 @@ async fn handle_incoming_message( allowed_routers: log.routers, }, ); - let _ = names.write().await.insert(log.node, log.name); + names.insert(log.node, log.name); } NetActions::QnsBatchUpdate(log_list) => { - let _ = print_tx + print_tx .send(Printout { verbosity: 1, content: format!( @@ -810,9 +1154,9 @@ async fn handle_incoming_message( log_list.len() ), }) - .await; + .await?; for log in log_list { - let _ = pki.write().await.insert( + pki.insert( log.name.clone(), Identity { name: log.name.clone(), @@ -826,125 +1170,12 @@ async fn handle_incoming_message( allowed_routers: log.routers, }, ); - let _ = names.write().await.insert(log.node, log.name); + names.insert(log.node, log.name); } } } } } - } -} - -/* - * networking utils - */ - -fn make_ws_url(our_ip: &str, ip: &str, port: &u16) -> Result { - // if we have the same public IP as target, route locally, - // otherwise they will appear offline due to loopback stuff - let ip = if our_ip == ip { "localhost" } else { ip }; - match url::Url::parse(&format!("ws://{}:{}/ws", ip, port)) { - Ok(v) => Ok(v), - Err(_) => Err(SendErrorKind::Offline), - } -} - -/* - * handshake utils - */ - -/// take in handshake and PKI identity, and confirm that the handshake is valid. -/// takes in optional nonce, which must be the one that connection initiator created. -fn validate_handshake( - handshake: &Handshake, - their_id: &Identity, -) -> Result>, String> { - let their_networking_key = signature::UnparsedPublicKey::new( - &signature::ED25519, - hex::decode(&strip_0x(&their_id.networking_key)) - .map_err(|_| "failed to decode networking key")?, - ); - - if !(their_networking_key - .verify( - // TODO use language-neutral serialization here too - &bincode::serialize(their_id).map_err(|_| "failed to serialize their identity")?, - &handshake.id_signature, - ) - .is_ok() - && their_networking_key - .verify( - &handshake.ephemeral_public_key, - &handshake.ephemeral_public_key_signature, - ) - .is_ok()) - { - // improper signatures on identity info, close connection - return Err("got improperly signed networking info".into()); - } - - match PublicKey::::from_sec1_bytes(&handshake.ephemeral_public_key) { - Ok(v) => return Ok(Arc::new(v)), - Err(_) => return Err("error".into()), - }; -} - -/// given an identity and networking key-pair, produces a handshake message along -/// with an ephemeral secret to be used in a specific connection. -fn make_secret_and_handshake( - our: &Identity, - keypair: Arc, - target: &str, - id: Option, -) -> (Arc>, Handshake) { - // produce ephemeral keys for DH exchange and subsequent symmetric encryption - let ephemeral_secret = Arc::new(EphemeralSecret::::random( - &mut rand::rngs::OsRng, - )); - let ephemeral_public_key = ephemeral_secret.public_key(); - // sign the ephemeral public key with our networking management key - let signed_pk = keypair - .sign(&ephemeral_public_key.to_sec1_bytes()) - .as_ref() - .to_vec(); - - // before signing our identity, convert router names to namehashes - // to match the exact onchain representation of our identity - let mut our_onchain_id = our.clone(); - our_onchain_id.allowed_routers = our - .allowed_routers - .clone() - .into_iter() - .map(|namehash| { - let hash = crate::namehash(&namehash); - let mut result = [0u8; 32]; - result.copy_from_slice(hash.as_bytes()); - format!("0x{}", hex::encode(result)) - }) - .collect(); - - // TODO use language-neutral serialization here too - let signed_id = keypair - .sign(&bincode::serialize(&our_onchain_id).unwrap()) - .as_ref() - .to_vec(); - - let handshake = Handshake { - id: id.unwrap_or(rand::random()), - from: our.name.clone(), - target: target.to_string(), - id_signature: signed_id, - ephemeral_public_key: ephemeral_public_key.to_sec1_bytes().to_vec(), - ephemeral_public_key_signature: signed_pk, - }; - - (ephemeral_secret, handshake) -} - -fn strip_0x(s: &str) -> String { - if s.starts_with("0x") { - s[2..].to_string() - } else { - s.to_string() + Ok(()) } } diff --git a/src/net/types.rs b/src/net/types.rs index 251c40f2..4141e718 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -1,71 +1,108 @@ use crate::types::*; -use anyhow::Result; -use elliptic_curve::ecdh::SharedSecret; -use ethers::prelude::k256::Secp256k1; +use futures::stream::{SplitSink, SplitStream}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, sync::Arc}; use tokio::net::TcpStream; -use tokio::sync::{mpsc, RwLock}; -use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; +use tokio::sync::mpsc::UnboundedSender; +use tokio_tungstenite::{tungstenite, MaybeTlsStream, WebSocketStream}; -pub type PeerKeys = Arc>)>>>; -pub type Peers = Arc>>; -pub type WebSocket = WebSocketStream>; -pub type MessageResult = Result; -pub type ErrorShuttle = mpsc::UnboundedSender; +/// Sent to a node when you want to connect directly to them. +/// Sent in the 'e, ee, s, es' and 's, se' phases of XX noise protocol pattern. +#[derive(Debug, Deserialize, Serialize)] +pub struct HandshakePayload { + pub name: NodeId, + // signature is created by their networking key, of their static key + // someone could reuse this signature, but then they will be unable + // to encrypt messages to us. + pub signature: Vec, + /// Set to true when you want them to act as a router for you, sending + /// messages from potentially many remote sources over this connection, + /// including from the router itself. + /// This is not relevant in a handshake sent from the receiver side. + pub proxy_request: bool, + pub protocol_version: u8, +} + +/// Sent to a node when you want them to connect you to an indirect node. +/// If the receiver of the request has an open connection to your target, +/// and is willing, they will send a message to the target prompting them +/// to build the other side of the connection, at which point they will +/// hold open a Passthrough for you two. +/// +/// Alternatively, if the receiver does not have an open connection but the +/// target is a direct node, they can create a Passthrough for you two if +/// they are willing to proxy for you. +/// +/// Sent in the 'e' phase of XX noise protocol pattern. +#[derive(Debug, Deserialize, Serialize)] +pub struct RoutingRequest { + pub source: NodeId, + // signature is created by their networking key, of the [target, router name].concat() + // someone could reuse this signature, and TODO need to make sure that's useless. + pub signature: Vec, + pub target: NodeId, + pub protocol_version: u8, +} + +pub enum Connection { + Peer(PeerConnection), + Passthrough(PassthroughConnection), + PendingPassthrough(PendingPassthroughConnection), +} + +pub struct PeerConnection { + pub noise: snow::TransportState, + pub buf: Vec, + pub write_stream: SplitSink>, tungstenite::Message>, + pub read_stream: SplitStream>>, +} + +pub struct PassthroughConnection { + pub write_stream_1: SplitSink>, tungstenite::Message>, + pub read_stream_1: SplitStream>>, + pub write_stream_2: SplitSink>, tungstenite::Message>, + pub read_stream_2: SplitStream>>, +} + +pub struct PendingPassthroughConnection { + pub target: NodeId, + pub write_stream: SplitSink>, tungstenite::Message>, + pub read_stream: SplitStream>>, +} + +// TODO upgrade from hashmaps +pub type Peers = HashMap>; +pub type PKINames = HashMap; // TODO maybe U256 to String +pub type OnchainPKI = HashMap; +pub type PendingPassthroughs = HashMap<(NodeId, NodeId), PendingPassthroughConnection>; -/// stored in mapping by their username pub struct Peer { pub identity: Identity, - // send messages here to have them encrypted and sent across an active connection - pub sender: mpsc::UnboundedSender<(PeerMessage, Option)>, - // send encrypted messages from this peer here to have them decrypted and sent to kernel - pub decrypter: mpsc::UnboundedSender<(Vec, ErrorShuttle)>, - pub socket_tx: mpsc::UnboundedSender<(NetworkMessage, Option)>, -} - -/// parsed from Binary websocket message -/// TODO add a version number somewhere in the serialized format!! -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum NetworkMessage { - Ack(u64), - Nack(u64), - Msg { - id: u64, - from: String, - to: String, - contents: Vec, - }, - Handshake(Handshake), - HandshakeAck(Handshake), - // only used in implementation, not part of protocol - Ping, - Pong, -} - -pub enum PeerMessage { - Raw(KernelMessage), - Net(NetworkMessage), -} - -/// contains identity and encryption keys, used in initial handshake. -/// parsed from Text websocket message -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Handshake { - pub id: u64, - pub from: String, - pub target: String, - pub id_signature: Vec, - pub ephemeral_public_key: Vec, - pub ephemeral_public_key_signature: Vec, + /// If true, we are routing for them and have a RoutingClientConnection + /// associated with them. We can send them prompts to establish Passthroughs. + pub routing_for: bool, + pub sender: UnboundedSender, } #[derive(Clone, Debug, Serialize, Deserialize)] pub enum NetActions { + /// Received from a router of ours when they have a new pending passthrough for us. + /// We should respond (if we desire) by using them to initialize a routed connection + /// with the NodeId given. + ConnectionRequest(NodeId), + /// can only receive from trusted source, for now just ourselves locally, + /// in the future could get from remote provider QnsUpdate(QnsUpdate), QnsBatchUpdate(Vec), } +/// For now, only sent in response to a ConnectionRequest +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum NetResponses { + Attempting(NodeId), + Rejected(NodeId), +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct QnsUpdate { pub name: String, // actual username / domain name diff --git a/src/net2/utils.rs b/src/net/utils.rs similarity index 99% rename from src/net2/utils.rs rename to src/net/utils.rs index addf5bbb..3b412ad6 100644 --- a/src/net2/utils.rs +++ b/src/net/utils.rs @@ -1,4 +1,4 @@ -use crate::net2::{types::*, MESSAGE_MAX_SIZE, TIMEOUT}; +use crate::net::{types::*, MESSAGE_MAX_SIZE, TIMEOUT}; use crate::types::*; use anyhow::{anyhow, Result}; use futures::stream::{SplitSink, SplitStream}; diff --git a/src/net2/mod.rs b/src/net2/mod.rs deleted file mode 100644 index 7894c041..00000000 --- a/src/net2/mod.rs +++ /dev/null @@ -1,1186 +0,0 @@ -use crate::net2::{types::*, utils::*}; -use crate::types::*; -use anyhow::{anyhow, Result}; -use futures::{SinkExt, StreamExt}; -use rand::seq::SliceRandom; -use ring::signature::Ed25519KeyPair; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; -use tokio::net::TcpListener; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; -use tokio::task::JoinSet; -use tokio::time; -use tokio_tungstenite::{accept_async, connect_async, MaybeTlsStream, WebSocketStream}; - -mod types; -mod utils; - -// only used in connection initialization, otherwise, nacks and Responses are only used for "timeouts" -const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); - -// 10 MB -- TODO analyze as desired, apps can always chunk data into many messages -const MESSAGE_MAX_SIZE: u32 = 10_485_800; - -/// Entry point from the main kernel task. Runs forever, spawns listener and sender tasks. -pub async fn networking( - our: Identity, - our_ip: String, - keypair: Arc, - kernel_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, - print_tx: PrintSender, - self_message_tx: MessageSender, - message_rx: MessageReceiver, -) -> Result<()> { - println!("networking!\r"); - println!("our identity: {:#?}\r", our); - // branch on whether we are a direct or indirect node - match &our.ws_routing { - None => { - // indirect node: run the indirect networking strategy - indirect_networking( - our, - our_ip, - keypair, - kernel_message_tx, - network_error_tx, - print_tx, - self_message_tx, - message_rx, - ) - .await - } - Some((ip, port)) => { - // direct node: run the direct networking strategy - if &our_ip != ip { - return Err(anyhow!( - "net: fatal error: IP address mismatch: {} != {}, update your QNS identity", - our_ip, - ip - )); - } - let tcp = match TcpListener::bind(format!("0.0.0.0:{}", port)).await { - Ok(tcp) => tcp, - Err(_e) => { - return Err(anyhow!( - "net: fatal error: can't listen on port {}, update your QNS identity or free up that port", - port, - )); - } - }; - direct_networking( - our, - our_ip, - tcp, - keypair, - kernel_message_tx, - network_error_tx, - print_tx, - self_message_tx, - message_rx, - ) - .await - } - } -} - -async fn indirect_networking( - our: Identity, - our_ip: String, - keypair: Arc, - kernel_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, - print_tx: PrintSender, - self_message_tx: MessageSender, - mut message_rx: MessageReceiver, -) -> Result<()> { - println!("indirect_networking\r"); - let mut pki: OnchainPKI = HashMap::new(); - let mut peers: Peers = HashMap::new(); - // mapping from QNS namehash to username - let mut names: PKINames = HashMap::new(); - - let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); - let mut active_routers = HashSet::::new(); - - // before opening up the main loop, go through our allowed routers - // and attempt to connect to all of them, saving the successfully - // connected-to ones in our router-set - connect_to_routers( - &our, - &our_ip, - &keypair, - &mut active_routers, - &pki, - &mut peers, - &mut peer_connections, - kernel_message_tx.clone(), - ) - .await; - - loop { - tokio::select! { - // 1. receive messages from kernel and send out over connections, - // making new connections through our router-set as needed - Some(km) = message_rx.recv() => { - // got a message from kernel to send out over the network - let target = &km.target.node; - // if the message is for us, it's either a protocol-level "hello" message, - // or a debugging command issued from our terminal. handle it here: - if target == &our.name { - match handle_local_message( - &our, - &our_ip, - &keypair, - km, - &mut peers, - &mut pki, - &mut peer_connections, - None, - None, - Some(&active_routers), - &mut names, - &kernel_message_tx, - &print_tx, - ) - .await { - Ok(()) => {}, - Err(e) => { - print_tx.send(Printout { - verbosity: 0, - content: format!("net: error handling local message: {}", e) - }).await?; - } - } - } - // if the message is for a peer we currently have a connection with, - // try to send it to them - else if let Some(peer) = peers.get_mut(target) { - peer.sender.send(km)?; - } - else if let Some(peer_id) = pki.get(target) { - // if the message is for a *direct* peer we don't have a connection with, - // try to establish a connection with them - // TODO: here, we can *choose* to use our routers so as not to reveal - // networking information about ourselves to the target. - if peer_id.ws_routing.is_some() { - match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id.clone(), - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer_name, peer.clone()); - peer.sender.send(km)?; - peer_connections.spawn(maintain_connection( - peer, - direct_conn, - peer_rx, - kernel_message_tx.clone(), - )); - } - Err(e) => { - println!("net: error initializing connection: {}\r", e); - error_offline(km, &network_error_tx).await?; - } - } - } - // if the message is for an *indirect* peer we don't have a connection with, - // do some routing: in a randomized order, go through their listed routers - // on chain and try to get one of them to build a proxied connection to - // this node for you - else { - let sent = time::timeout(TIMEOUT, - init_connection_via_router( - &our, - &our_ip, - &keypair, - km.clone(), - peer_id, - &pki, - &names, - &mut peers, - &mut peer_connections, - kernel_message_tx.clone() - )).await; - if !sent.unwrap_or(false) { - // none of the routers worked! - println!("net: error initializing routed connection\r"); - error_offline(km, &network_error_tx).await?; - } - } - } - // peer cannot be found in PKI, throw an offline error - else { - error_offline(km, &network_error_tx).await?; - } - } - // 2. deal with active connections that die by removing the associated peer - // if the peer is one of our routers, remove them from router-set - Some(Ok((dead_peer, maybe_resend))) = peer_connections.join_next() => { - peers.remove(&dead_peer); - active_routers.remove(&dead_peer); - match maybe_resend { - None => {}, - Some(km) => { - self_message_tx.send(km).await?; - } - } - } - // 3. periodically attempt to connect to any allowed routers that we - // are not connected to - _ = time::sleep(time::Duration::from_secs(3)) => { - connect_to_routers( - &our, - &our_ip, - &keypair, - &mut active_routers, - &pki, - &mut peers, - &mut peer_connections, - kernel_message_tx.clone(), - ) - .await; - } - } - } -} - -async fn connect_to_routers( - our: &Identity, - our_ip: &str, - keypair: &Ed25519KeyPair, - active_routers: &mut HashSet, - pki: &OnchainPKI, - peers: &mut Peers, - peer_connections: &mut JoinSet<(NodeId, Option)>, - kernel_message_tx: MessageSender, -) { - for router in &our.allowed_routers { - if active_routers.contains(router) { - continue; - } - let Some(router_id) = pki.get(router) else { - continue; - }; - match init_connection(our, our_ip, router_id, keypair, None, true).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: router_id.clone(), - routing_for: false, - sender: peer_tx, - }); - println!("net: connected to router {}\r", peer_name); - peers.insert(peer_name.clone(), peer.clone()); - active_routers.insert(peer_name); - peer_connections.spawn(maintain_connection( - peer, - direct_conn, - peer_rx, - kernel_message_tx.clone(), - )); - } - Err(_e) => continue, - } - } -} - -async fn direct_networking( - our: Identity, - our_ip: String, - tcp: TcpListener, - keypair: Arc, - kernel_message_tx: MessageSender, - network_error_tx: NetworkErrorSender, - print_tx: PrintSender, - self_message_tx: MessageSender, - mut message_rx: MessageReceiver, -) -> Result<()> { - println!("direct_networking\r"); - let mut pki: OnchainPKI = HashMap::new(); - let mut peers: Peers = HashMap::new(); - // mapping from QNS namehash to username - let mut names: PKINames = HashMap::new(); - - let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); - let mut forwarding_connections = JoinSet::<()>::new(); - let mut pending_passthroughs: PendingPassthroughs = HashMap::new(); - - loop { - tokio::select! { - // 1. receive messages from kernel and send out over our connections, - // making new connections as needed - Some(km) = message_rx.recv() => { - // got a message from kernel to send out over the network - let target = &km.target.node; - // if the message is for us, it's either a protocol-level "hello" message, - // or a debugging command issued from our terminal. handle it here: - if target == &our.name { - match handle_local_message( - &our, - &our_ip, - &keypair, - km, - &mut peers, - &mut pki, - &mut peer_connections, - Some(&mut pending_passthroughs), - Some(&forwarding_connections), - None, - &mut names, - &kernel_message_tx, - &print_tx, - ) - .await { - Ok(()) => {}, - Err(e) => { - print_tx.send(Printout { - verbosity: 0, - content: format!("net: error handling local message: {}", e) - }).await?; - } - } - } - // if the message is for a peer we currently have a connection with, - // try to send it to them - else if let Some(peer) = peers.get_mut(target) { - peer.sender.send(km)?; - } - else if let Some(peer_id) = pki.get(target) { - // if the message is for a *direct* peer we don't have a connection with, - // try to establish a connection with them - if peer_id.ws_routing.is_some() { - match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id.clone(), - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer_name, peer.clone()); - peer.sender.send(km)?; - peer_connections.spawn(maintain_connection( - peer, - direct_conn, - peer_rx, - kernel_message_tx.clone(), - )); - } - Err(e) => { - println!("net: error initializing connection: {}\r", e); - error_offline(km, &network_error_tx).await?; - } - } - } - // if the message is for an *indirect* peer we don't have a connection with, - // do some routing: in a randomized order, go through their listed routers - // on chain and try to get one of them to build a proxied connection to - // this node for you - else { - let sent = time::timeout(TIMEOUT, - init_connection_via_router( - &our, - &our_ip, - &keypair, - km.clone(), - peer_id, - &pki, - &names, - &mut peers, - &mut peer_connections, - kernel_message_tx.clone() - )).await; - if !sent.unwrap_or(false) { - // none of the routers worked! - println!("net: error initializing routed connection\r"); - error_offline(km, &network_error_tx).await?; - } - } - } - // peer cannot be found in PKI, throw an offline error - else { - error_offline(km, &network_error_tx).await?; - } - } - // 2. receive incoming TCP connections - Ok((stream, _socket_addr)) = tcp.accept() => { - // TODO we can perform some amount of validation here - // to prevent some amount of potential DDoS attacks. - // can also block based on socket_addr - match accept_async(MaybeTlsStream::Plain(stream)).await { - Ok(websocket) => { - let (peer_id, routing_for, conn) = - match recv_connection( - &our, - &our_ip, - &pki, - &peers, - &mut pending_passthroughs, - &keypair, - websocket).await - { - Ok(res) => res, - Err(e) => { - println!("net: recv_connection failed: {e}\r"); - continue; - } - }; - // if conn is direct, add peer - // if passthrough, add to our forwarding connections joinset - match conn { - Connection::Peer(peer_conn) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id, - routing_for, - sender: peer_tx, - }); - peers.insert(peer.identity.name.clone(), peer.clone()); - peer_connections.spawn(maintain_connection( - peer, - peer_conn, - peer_rx, - kernel_message_tx.clone(), - )); - } - Connection::Passthrough(passthrough_conn) => { - forwarding_connections.spawn(maintain_passthrough( - passthrough_conn, - )); - } - Connection::PendingPassthrough(pending_conn) => { - pending_passthroughs.insert( - (peer_id.name.clone(), pending_conn.target.clone()), - pending_conn - ); - } - } - } - // ignore connections we failed to accept...? - Err(_) => {} - } - } - // 3. deal with active connections that die by removing the associated peer - Some(Ok((dead_peer, maybe_resend))) = peer_connections.join_next() => { - peers.remove(&dead_peer); - match maybe_resend { - None => {}, - Some(km) => { - self_message_tx.send(km).await?; - } - } - } - } - } -} - -async fn init_connection_via_router( - our: &Identity, - our_ip: &str, - keypair: &Ed25519KeyPair, - km: KernelMessage, - peer_id: &Identity, - pki: &OnchainPKI, - names: &PKINames, - peers: &mut Peers, - peer_connections: &mut JoinSet<(NodeId, Option)>, - kernel_message_tx: MessageSender, -) -> bool { - println!("init_connection_via_router\r"); - let routers_shuffled = { - let mut routers = peer_id.allowed_routers.clone(); - routers.shuffle(&mut rand::thread_rng()); - routers - }; - for router_namehash in &routers_shuffled { - let Some(router_name) = names.get(router_namehash) else { - continue; - }; - let router_id = match pki.get(router_name) { - None => continue, - Some(id) => id, - }; - match init_connection(&our, &our_ip, peer_id, &keypair, Some(router_id), false).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id.clone(), - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer_name, peer.clone()); - peer.sender.send(km).unwrap(); - peer_connections.spawn(maintain_connection( - peer, - direct_conn, - peer_rx, - kernel_message_tx.clone(), - )); - return true; - } - Err(_) => continue, - } - } - return false; -} - -async fn maintain_connection( - peer: Arc, - mut conn: PeerConnection, - mut peer_rx: UnboundedReceiver, - kernel_message_tx: MessageSender, - // network_error_tx: NetworkErrorSender, - // print_tx: PrintSender, -) -> (NodeId, Option) { - println!("maintain_connection\r"); - loop { - tokio::select! { - recv_result = recv_uqbar_message(&mut conn) => { - match recv_result { - Ok(km) => { - if km.source.node != peer.identity.name { - println!("net: got message with spoofed source\r"); - return (peer.identity.name.clone(), None) - } - kernel_message_tx.send(km).await.expect("net error: fatal: kernel died"); - } - Err(e) => { - println!("net: error receiving message: {}\r", e); - return (peer.identity.name.clone(), None) - } - } - }, - maybe_recv = peer_rx.recv() => { - match maybe_recv { - Some(km) => { - // TODO error handle - match send_uqbar_message(&km, &mut conn).await { - Ok(()) => continue, - Err(e) => { - println!("net: error sending message: {}\r", e); - return (peer.identity.name.clone(), Some(km)) - } - } - } - None => { - println!("net: peer disconnected\r"); - return (peer.identity.name.clone(), None) - } - } - }, - } - } -} - -/// match the streams -/// TODO optimize performance of this -async fn maintain_passthrough(mut conn: PassthroughConnection) { - println!("maintain_passthrough\r"); - loop { - tokio::select! { - maybe_recv = conn.read_stream_1.next() => { - match maybe_recv { - Some(Ok(msg)) => { - conn.write_stream_2.send(msg).await.expect("net error: fatal: kernel died"); - } - _ => { - println!("net: passthrough broke\r"); - return - } - } - }, - maybe_recv = conn.read_stream_2.next() => { - match maybe_recv { - Some(Ok(msg)) => { - conn.write_stream_1.send(msg).await.expect("net error: fatal: kernel died"); - } - _ => { - println!("net: passthrough broke\r"); - return - } - } - }, - } - } -} - -async fn recv_connection( - our: &Identity, - our_ip: &str, - pki: &OnchainPKI, - peers: &Peers, - pending_passthroughs: &mut PendingPassthroughs, - keypair: &Ed25519KeyPair, - websocket: WebSocketStream>, -) -> Result<(Identity, bool, Connection)> { - println!("recv_connection\r"); - let mut buf = vec![0u8; 65535]; - let (mut noise, our_static_key) = build_responder(); - let (mut write_stream, mut read_stream) = websocket.split(); - - // before we begin XX handshake pattern, check first message over socket - let first_message = &ws_recv(&mut read_stream).await?; - - // if the first message contains a "routing request", - // we see if the target is someone we are actively routing for, - // and create a Passthrough connection if so. - // a Noise 'e' message with have len 32 - if first_message.len() != 32 { - let (their_id, target_name) = validate_routing_request(&our.name, &first_message, pki)?; - let (id, conn) = create_passthrough( - our, - our_ip, - their_id, - target_name, - pki, - peers, - pending_passthroughs, - write_stream, - read_stream, - ) - .await?; - return Ok((id, false, conn)); - } - - // <- e - noise.read_message(first_message, &mut buf)?; - - // -> e, ee, s, es - send_uqbar_handshake( - &our, - keypair, - &our_static_key, - &mut noise, - &mut buf, - &mut write_stream, - false, - ) - .await?; - - // <- s, se - let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; - - // now validate this handshake payload against the QNS PKI - let their_id = pki - .get(&their_handshake.name) - .ok_or(anyhow!("unknown QNS name"))?; - validate_handshake( - &their_handshake, - noise - .get_remote_static() - .ok_or(anyhow!("noise error: missing remote pubkey"))?, - their_id, - )?; - - // Transition the state machine into transport mode now that the handshake is complete. - let noise = noise.into_transport_mode()?; - println!("handshake complete, noise session received\r"); - - // TODO if their handshake indicates they want us to proxy - // for them (aka act as a router for them) we can choose - // whether to do so here. - Ok(( - their_id.clone(), - their_handshake.proxy_request, - Connection::Peer(PeerConnection { - noise, - buf, - write_stream, - read_stream, - }), - )) -} - -async fn recv_connection_via_router( - our: &Identity, - our_ip: &str, - their_name: &str, - pki: &OnchainPKI, - keypair: &Ed25519KeyPair, - router: &Identity, -) -> Result<(Identity, PeerConnection)> { - println!("recv_connection_via_router\r"); - let mut buf = vec![0u8; 65535]; - let (mut noise, our_static_key) = build_responder(); - - let Some((ref ip, ref port)) = router.ws_routing else { - return Err(anyhow!("router has no routing information")); - }; - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; - let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await else { - return Err(anyhow!("failed to connect to target")); - }; - let (mut write_stream, mut read_stream) = websocket.split(); - - // before beginning XX handshake pattern, send a routing request - let message = bincode::serialize(&RoutingRequest { - source: our.name.clone(), - signature: keypair - .sign([their_name, router.name.as_str()].concat().as_bytes()) - .as_ref() - .to_vec(), - target: their_name.to_string(), - protocol_version: 1, - })?; - ws_send(&mut write_stream, &message).await?; - - // <- e - noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; - - // -> e, ee, s, es - send_uqbar_handshake( - &our, - keypair, - &our_static_key, - &mut noise, - &mut buf, - &mut write_stream, - false, - ) - .await?; - - // <- s, se - let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; - - // now validate this handshake payload against the QNS PKI - let their_id = pki - .get(&their_handshake.name) - .ok_or(anyhow!("unknown QNS name"))?; - validate_handshake( - &their_handshake, - noise - .get_remote_static() - .ok_or(anyhow!("noise error: missing remote pubkey"))?, - their_id, - )?; - - // Transition the state machine into transport mode now that the handshake is complete. - let noise = noise.into_transport_mode()?; - println!("handshake complete, noise session received\r"); - - Ok(( - their_id.clone(), - PeerConnection { - noise, - buf, - write_stream, - read_stream, - }, - )) -} - -async fn init_connection( - our: &Identity, - our_ip: &str, - peer_id: &Identity, - keypair: &Ed25519KeyPair, - use_router: Option<&Identity>, - proxy_request: bool, -) -> Result<(String, PeerConnection)> { - println!("init_connection\r"); - let mut buf = vec![0u8; 65535]; - let (mut noise, our_static_key) = build_initiator(); - - let (mut write_stream, mut read_stream) = match use_router { - None => { - let Some((ref ip, ref port)) = peer_id.ws_routing else { - return Err(anyhow!("target has no routing information")); - }; - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; - let Ok(Ok((websocket, _response))) = - time::timeout(TIMEOUT, connect_async(ws_url)).await - else { - return Err(anyhow!("failed to connect to target")); - }; - websocket.split() - } - Some(router_id) => { - let Some((ref ip, ref port)) = router_id.ws_routing else { - return Err(anyhow!("router has no routing information")); - }; - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; - let Ok(Ok((websocket, _response))) = - time::timeout(TIMEOUT, connect_async(ws_url)).await - else { - return Err(anyhow!("failed to connect to target")); - }; - websocket.split() - } - }; - - // if this is a routed request, before starting XX handshake pattern, send a - // routing request message over socket - if use_router.is_some() { - let message = bincode::serialize(&RoutingRequest { - source: our.name.clone(), - signature: keypair - .sign( - [&peer_id.name, use_router.unwrap().name.as_str()] - .concat() - .as_bytes(), - ) - .as_ref() - .to_vec(), - target: peer_id.name.clone(), - protocol_version: 1, - })?; - ws_send(&mut write_stream, &message).await?; - } - - // -> e - let len = noise.write_message(&[], &mut buf)?; - ws_send(&mut write_stream, &buf[..len]).await?; - - // <- e, ee, s, es - let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; - - // now validate this handshake payload against the QNS PKI - validate_handshake( - &their_handshake, - noise - .get_remote_static() - .ok_or(anyhow!("noise error: missing remote pubkey"))?, - peer_id, - )?; - - // -> s, se - send_uqbar_handshake( - &our, - keypair, - &our_static_key, - &mut noise, - &mut buf, - &mut write_stream, - proxy_request, - ) - .await?; - - let noise = noise.into_transport_mode()?; - println!("handshake complete, noise session initiated\r"); - - Ok(( - their_handshake.name, - PeerConnection { - noise, - buf, - write_stream, - read_stream, - }, - )) -} - -/// net module only handles incoming local requests, will never return a response -async fn handle_local_message( - our: &Identity, - our_ip: &str, - keypair: &Ed25519KeyPair, - km: KernelMessage, - peers: &mut Peers, - pki: &mut OnchainPKI, - peer_connections: &mut JoinSet<(NodeId, Option)>, - pending_passthroughs: Option<&mut PendingPassthroughs>, - forwarding_connections: Option<&JoinSet<()>>, - active_routers: Option<&HashSet>, - names: &mut PKINames, - kernel_message_tx: &MessageSender, - print_tx: &PrintSender, -) -> Result<()> { - println!("handle_local_message\r"); - let ipc = match km.message { - Message::Request(request) => request.ipc, - Message::Response((response, _context)) => { - // these are received as a router, when we send ConnectionRequests - // to a node we do routing for. - match serde_json::from_slice::(&response.ipc)? { - NetResponses::Attempting(_) => { - // TODO anything here? - } - NetResponses::Rejected(to) => { - // drop from our pending map - // this will drop the socket, causing initiator to see it as failed - pending_passthroughs - .ok_or(anyhow!("got net response as non-router"))? - .remove(&(to, km.source.node)); - } - } - return Ok(()); - } - }; - - if km.source.node != our.name { - if let Ok(act) = serde_json::from_slice::(&ipc) { - match act { - NetActions::QnsBatchUpdate(_) | NetActions::QnsUpdate(_) => { - // for now, we don't get these from remote. - } - NetActions::ConnectionRequest(from) => { - // someone wants to open a passthrough with us through a router! - // if we are an indirect node, and source is one of our routers, - // respond by attempting to init a matching passthrough. - // TODO can discriminate more here.. - if our.allowed_routers.contains(&km.source.node) { - let Ok((peer_id, peer_conn)) = time::timeout( - TIMEOUT, - recv_connection_via_router( - our, - our_ip, - &from, - pki, - keypair, - &peers - .get(&km.source.node) - .ok_or(anyhow!("unknown router"))? - .identity, - ), - ) - .await? - else { - return Err(anyhow!("someone tried to connect to us but it timed out")); - }; - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id, - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer.identity.name.clone(), peer.clone()); - peer_connections.spawn(maintain_connection( - peer, - peer_conn, - peer_rx, - kernel_message_tx.clone(), - )); - } else { - kernel_message_tx - .send(KernelMessage { - id: km.id, - source: Address { - node: our.name.clone(), - process: ProcessId::from_str("net:sys:uqbar").unwrap(), - }, - target: km.rsvp.unwrap_or(km.source), - rsvp: None, - message: Message::Response(( - Response { - inherit: false, - ipc: serde_json::to_vec(&NetResponses::Rejected(from))?, - metadata: None, - }, - None, - )), - payload: None, - signed_capabilities: None, - }) - .await?; - } - } - } - return Ok(()); - }; - // if we can't parse this to a netaction, treat it as a hello and print it - // respond to a text message with a simple "delivered" response - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "\x1b[3;32m{}: {}\x1b[0m", - km.source.node, - std::str::from_utf8(&ipc).unwrap_or("!!message parse error!!") - ), - }) - .await?; - kernel_message_tx - .send(KernelMessage { - id: km.id, - source: Address { - node: our.name.clone(), - process: ProcessId::from_str("net:sys:uqbar").unwrap(), - }, - target: km.rsvp.unwrap_or(km.source), - rsvp: None, - message: Message::Response(( - Response { - inherit: false, - ipc: "delivered".as_bytes().to_vec(), - metadata: None, - }, - None, - )), - payload: None, - signed_capabilities: None, - }) - .await?; - Ok(()) - } else { - // available commands: "peers", "pki", "names", "diagnostics" - // first parse as raw string, then deserialize to NetActions object - match std::str::from_utf8(&ipc) { - Ok("peers") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("{:#?}", peers.keys()), - }) - .await?; - } - Ok("pki") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("{:#?}", pki), - }) - .await?; - } - Ok("names") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("{:#?}", names), - }) - .await?; - } - Ok("diagnostics") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("our Identity: {:#?}", our), - }) - .await?; - print_tx - .send(Printout { - verbosity: 0, - content: format!("we have connections with peers: {:#?}", peers.keys()), - }) - .await?; - print_tx - .send(Printout { - verbosity: 0, - content: format!("we have {} entries in the PKI", pki.len()), - }) - .await?; - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} open peer connections", - peer_connections.len() - ), - }) - .await?; - if pending_passthroughs.is_some() { - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} pending passthrough connections", - pending_passthroughs.unwrap().len() - ), - }) - .await?; - } - if forwarding_connections.is_some() { - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} open passthrough connections", - forwarding_connections.unwrap().len() - ), - }) - .await?; - } - if active_routers.is_some() { - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} active routers", - active_routers.unwrap().len() - ), - }) - .await?; - } - } - _ => { - let Ok(act) = serde_json::from_slice::(&ipc) else { - print_tx - .send(Printout { - verbosity: 0, - content: "net: got unknown command".into(), - }) - .await?; - return Ok(()); - }; - match act { - NetActions::ConnectionRequest(_) => { - // we shouldn't receive these from ourselves. - } - NetActions::QnsUpdate(log) => { - print_tx - .send(Printout { - verbosity: 1, - content: format!("net: got QNS update for {}", log.name), - }) - .await?; - - pki.insert( - log.name.clone(), - Identity { - name: log.name.clone(), - networking_key: log.public_key, - ws_routing: if log.ip == "0.0.0.0".to_string() || log.port == 0 { - None - } else { - Some((log.ip, log.port)) - }, - allowed_routers: log.routers, - }, - ); - names.insert(log.node, log.name); - } - NetActions::QnsBatchUpdate(log_list) => { - print_tx - .send(Printout { - verbosity: 1, - content: format!( - "net: got QNS update with {} peers", - log_list.len() - ), - }) - .await?; - for log in log_list { - pki.insert( - log.name.clone(), - Identity { - name: log.name.clone(), - networking_key: log.public_key, - ws_routing: if log.ip == "0.0.0.0".to_string() || log.port == 0 - { - None - } else { - Some((log.ip, log.port)) - }, - allowed_routers: log.routers, - }, - ); - names.insert(log.node, log.name); - } - } - } - } - } - Ok(()) - } -} diff --git a/src/net2/types.rs b/src/net2/types.rs deleted file mode 100644 index 4141e718..00000000 --- a/src/net2/types.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::types::*; -use futures::stream::{SplitSink, SplitStream}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, sync::Arc}; -use tokio::net::TcpStream; -use tokio::sync::mpsc::UnboundedSender; -use tokio_tungstenite::{tungstenite, MaybeTlsStream, WebSocketStream}; - -/// Sent to a node when you want to connect directly to them. -/// Sent in the 'e, ee, s, es' and 's, se' phases of XX noise protocol pattern. -#[derive(Debug, Deserialize, Serialize)] -pub struct HandshakePayload { - pub name: NodeId, - // signature is created by their networking key, of their static key - // someone could reuse this signature, but then they will be unable - // to encrypt messages to us. - pub signature: Vec, - /// Set to true when you want them to act as a router for you, sending - /// messages from potentially many remote sources over this connection, - /// including from the router itself. - /// This is not relevant in a handshake sent from the receiver side. - pub proxy_request: bool, - pub protocol_version: u8, -} - -/// Sent to a node when you want them to connect you to an indirect node. -/// If the receiver of the request has an open connection to your target, -/// and is willing, they will send a message to the target prompting them -/// to build the other side of the connection, at which point they will -/// hold open a Passthrough for you two. -/// -/// Alternatively, if the receiver does not have an open connection but the -/// target is a direct node, they can create a Passthrough for you two if -/// they are willing to proxy for you. -/// -/// Sent in the 'e' phase of XX noise protocol pattern. -#[derive(Debug, Deserialize, Serialize)] -pub struct RoutingRequest { - pub source: NodeId, - // signature is created by their networking key, of the [target, router name].concat() - // someone could reuse this signature, and TODO need to make sure that's useless. - pub signature: Vec, - pub target: NodeId, - pub protocol_version: u8, -} - -pub enum Connection { - Peer(PeerConnection), - Passthrough(PassthroughConnection), - PendingPassthrough(PendingPassthroughConnection), -} - -pub struct PeerConnection { - pub noise: snow::TransportState, - pub buf: Vec, - pub write_stream: SplitSink>, tungstenite::Message>, - pub read_stream: SplitStream>>, -} - -pub struct PassthroughConnection { - pub write_stream_1: SplitSink>, tungstenite::Message>, - pub read_stream_1: SplitStream>>, - pub write_stream_2: SplitSink>, tungstenite::Message>, - pub read_stream_2: SplitStream>>, -} - -pub struct PendingPassthroughConnection { - pub target: NodeId, - pub write_stream: SplitSink>, tungstenite::Message>, - pub read_stream: SplitStream>>, -} - -// TODO upgrade from hashmaps -pub type Peers = HashMap>; -pub type PKINames = HashMap; // TODO maybe U256 to String -pub type OnchainPKI = HashMap; -pub type PendingPassthroughs = HashMap<(NodeId, NodeId), PendingPassthroughConnection>; - -pub struct Peer { - pub identity: Identity, - /// If true, we are routing for them and have a RoutingClientConnection - /// associated with them. We can send them prompts to establish Passthroughs. - pub routing_for: bool, - pub sender: UnboundedSender, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum NetActions { - /// Received from a router of ours when they have a new pending passthrough for us. - /// We should respond (if we desire) by using them to initialize a routed connection - /// with the NodeId given. - ConnectionRequest(NodeId), - /// can only receive from trusted source, for now just ourselves locally, - /// in the future could get from remote provider - QnsUpdate(QnsUpdate), - QnsBatchUpdate(Vec), -} - -/// For now, only sent in response to a ConnectionRequest -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum NetResponses { - Attempting(NodeId), - Rejected(NodeId), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct QnsUpdate { - pub name: String, // actual username / domain name - pub owner: String, - pub node: String, // hex namehash of node - pub public_key: String, - pub ip: String, - pub port: u16, - pub routers: Vec, -} From d1634a681b3b0242bb753cdbc65a1248fd947c8b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:44:13 +0000 Subject: [PATCH 16/40] Format Rust code using rustfmt --- src/net/mod.rs | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/net/mod.rs b/src/net/mod.rs index bcdb31ef..f4c718b5 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -715,8 +715,7 @@ async fn recv_connection_via_router( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await - else { + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; let (mut write_stream, mut read_stream) = websocket.split(); @@ -798,7 +797,8 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + let Ok(Ok((websocket, _response))) = + time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; @@ -811,7 +811,8 @@ async fn init_connection( let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { return Err(anyhow!("failed to parse websocket url")); }; - let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await + let Ok(Ok((websocket, _response))) = + time::timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; @@ -930,20 +931,24 @@ async fn handle_local_message( // respond by attempting to init a matching passthrough. // TODO can discriminate more here.. if our.allowed_routers.contains(&km.source.node) { - let Ok((peer_id, peer_conn)) = time::timeout(TIMEOUT, - recv_connection_via_router( - our, - our_ip, - &from, - pki, - keypair, - &peers - .get(&km.source.node) - .ok_or(anyhow!("unknown router"))? - .identity, - )).await? else { - return Err(anyhow!("someone tried to connect to us but it timed out")) - }; + let Ok((peer_id, peer_conn)) = time::timeout( + TIMEOUT, + recv_connection_via_router( + our, + our_ip, + &from, + pki, + keypair, + &peers + .get(&km.source.node) + .ok_or(anyhow!("unknown router"))? + .identity, + ), + ) + .await? + else { + return Err(anyhow!("someone tried to connect to us but it timed out")); + }; let (peer_tx, peer_rx) = unbounded_channel::(); let peer = Arc::new(Peer { identity: peer_id, From b4ffb5883121368cb1f35f7b699114c29ac8469b Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Tue, 31 Oct 2023 16:27:41 -0400 Subject: [PATCH 17/40] cleanup --- src/main.rs | 10 +++- src/net/mod.rs | 153 ++++++++++++++++++++++------------------------- src/net/types.rs | 2 +- src/net/utils.rs | 2 +- 4 files changed, 80 insertions(+), 87 deletions(-) diff --git a/src/main.rs b/src/main.rs index c65a7e0f..e57ff2b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,11 @@ const CAP_CHANNEL_CAPACITY: usize = 1_000; const VERSION: &str = env!("CARGO_PKG_VERSION"); +/// This can and should be an environment variable / setting. It configures networking +/// such that indirect nodes always use routers, even when target is a direct node, +/// such that only their routers can ever see their physical networking details. +const REVEAL_IP: bool = true; + #[tokio::main] async fn main() { // For use with https://github.com/tokio-rs/console @@ -263,6 +268,7 @@ async fn main() { print_sender.clone(), net_message_sender, net_message_receiver, + REVEAL_IP, )); tasks.spawn(filesystem::fs_sender( our.name.clone(), @@ -320,10 +326,10 @@ async fn main() { "\x1b[38;5;196muh oh, a kernel process crashed: {}\x1b[0m", e ) - // TODO restart the task + // TODO restart the task? } else { format!("what does this mean???") - // TODO restart the task + // TODO restart the task? } } quit = terminal::terminal( diff --git a/src/net/mod.rs b/src/net/mod.rs index f4c718b5..36563344 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -20,7 +20,8 @@ mod utils; // only used in connection initialization, otherwise, nacks and Responses are only used for "timeouts" const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); -// 10 MB -- TODO analyze as desired, apps can always chunk data into many messages +/// 10 MB -- TODO analyze as desired, apps can always chunk data into many messages +/// note that this only applies to cross-network messages, not local ones. const MESSAGE_MAX_SIZE: u32 = 10_485_800; /// Entry point from the main kernel task. Runs forever, spawns listener and sender tasks. @@ -33,6 +34,7 @@ pub async fn networking( print_tx: PrintSender, self_message_tx: MessageSender, message_rx: MessageReceiver, + reveal_ip: bool, ) -> Result<()> { println!("networking!\r"); println!("our identity: {:#?}\r", our); @@ -49,6 +51,7 @@ pub async fn networking( print_tx, self_message_tx, message_rx, + reveal_ip, ) .await } @@ -95,6 +98,7 @@ async fn indirect_networking( print_tx: PrintSender, self_message_tx: MessageSender, mut message_rx: MessageReceiver, + reveal_ip: bool, ) -> Result<()> { println!("indirect_networking\r"); let mut pki: OnchainPKI = HashMap::new(); @@ -105,21 +109,6 @@ async fn indirect_networking( let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); let mut active_routers = HashSet::::new(); - // before opening up the main loop, go through our allowed routers - // and attempt to connect to all of them, saving the successfully - // connected-to ones in our router-set - connect_to_routers( - &our, - &our_ip, - &keypair, - &mut active_routers, - &pki, - &mut peers, - &mut peer_connections, - kernel_message_tx.clone(), - ) - .await; - loop { tokio::select! { // 1. receive messages from kernel and send out over connections, @@ -163,9 +152,9 @@ async fn indirect_networking( else if let Some(peer_id) = pki.get(target) { // if the message is for a *direct* peer we don't have a connection with, // try to establish a connection with them - // TODO: here, we can *choose* to use our routers so as not to reveal + // here, we can *choose* to use our routers so as not to reveal // networking information about ourselves to the target. - if peer_id.ws_routing.is_some() { + if peer_id.ws_routing.is_some() && reveal_ip { match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { Ok((peer_name, direct_conn)) => { let (peer_tx, peer_rx) = unbounded_channel::(); @@ -181,6 +170,7 @@ async fn indirect_networking( direct_conn, peer_rx, kernel_message_tx.clone(), + print_tx.clone(), )); } Err(e) => { @@ -190,6 +180,7 @@ async fn indirect_networking( } } // if the message is for an *indirect* peer we don't have a connection with, + // or we want to protect our node's physical networking details from non-routers, // do some routing: in a randomized order, go through their listed routers // on chain and try to get one of them to build a proxied connection to // this node for you @@ -205,7 +196,8 @@ async fn indirect_networking( &names, &mut peers, &mut peer_connections, - kernel_message_tx.clone() + kernel_message_tx.clone(), + print_tx.clone(), )).await; if !sent.unwrap_or(false) { // none of the routers worked! @@ -234,62 +226,43 @@ async fn indirect_networking( // 3. periodically attempt to connect to any allowed routers that we // are not connected to _ = time::sleep(time::Duration::from_secs(3)) => { - connect_to_routers( - &our, - &our_ip, - &keypair, - &mut active_routers, - &pki, - &mut peers, - &mut peer_connections, - kernel_message_tx.clone(), - ) - .await; + if active_routers.len() == our.allowed_routers.len() { + continue; + } + for router in &our.allowed_routers { + if active_routers.contains(router) { + continue; + } + let Some(router_id) = pki.get(router) else { + continue; + }; + match init_connection(&our, &our_ip, router_id, &keypair, None, true).await { + Ok((peer_name, direct_conn)) => { + let (peer_tx, peer_rx) = unbounded_channel::(); + let peer = Arc::new(Peer { + identity: router_id.clone(), + routing_for: false, + sender: peer_tx, + }); + println!("net: connected to router {}\r", peer_name); + peers.insert(peer_name.clone(), peer.clone()); + active_routers.insert(peer_name); + peer_connections.spawn(maintain_connection( + peer, + direct_conn, + peer_rx, + kernel_message_tx.clone(), + print_tx.clone(), + )); + } + Err(_e) => continue, + } + } } } } } -async fn connect_to_routers( - our: &Identity, - our_ip: &str, - keypair: &Ed25519KeyPair, - active_routers: &mut HashSet, - pki: &OnchainPKI, - peers: &mut Peers, - peer_connections: &mut JoinSet<(NodeId, Option)>, - kernel_message_tx: MessageSender, -) { - for router in &our.allowed_routers { - if active_routers.contains(router) { - continue; - } - let Some(router_id) = pki.get(router) else { - continue; - }; - match init_connection(our, our_ip, router_id, keypair, None, true).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: router_id.clone(), - routing_for: false, - sender: peer_tx, - }); - println!("net: connected to router {}\r", peer_name); - peers.insert(peer_name.clone(), peer.clone()); - active_routers.insert(peer_name); - peer_connections.spawn(maintain_connection( - peer, - direct_conn, - peer_rx, - kernel_message_tx.clone(), - )); - } - Err(_e) => continue, - } - } -} - async fn direct_networking( our: Identity, our_ip: String, @@ -370,6 +343,7 @@ async fn direct_networking( direct_conn, peer_rx, kernel_message_tx.clone(), + print_tx.clone(), )); } Err(e) => { @@ -394,7 +368,8 @@ async fn direct_networking( &names, &mut peers, &mut peer_connections, - kernel_message_tx.clone() + kernel_message_tx.clone(), + print_tx.clone(), )).await; if !sent.unwrap_or(false) { // none of the routers worked! @@ -431,8 +406,11 @@ async fn direct_networking( continue; } }; - // if conn is direct, add peer - // if passthrough, add to our forwarding connections joinset + // TODO if their handshake indicates they want us to proxy + // for them (aka act as a router for them) we can choose + // whether to do so here! + // if conn is direct, add peer. if passthrough, add to our + // forwarding connections joinset match conn { Connection::Peer(peer_conn) => { let (peer_tx, peer_rx) = unbounded_channel::(); @@ -447,6 +425,7 @@ async fn direct_networking( peer_conn, peer_rx, kernel_message_tx.clone(), + print_tx.clone(), )); } Connection::Passthrough(passthrough_conn) => { @@ -491,6 +470,7 @@ async fn init_connection_via_router( peers: &mut Peers, peer_connections: &mut JoinSet<(NodeId, Option)>, kernel_message_tx: MessageSender, + print_tx: PrintSender, ) -> bool { println!("init_connection_via_router\r"); let routers_shuffled = { @@ -521,6 +501,7 @@ async fn init_connection_via_router( direct_conn, peer_rx, kernel_message_tx.clone(), + print_tx.clone(), )); return true; } @@ -535,8 +516,7 @@ async fn maintain_connection( mut conn: PeerConnection, mut peer_rx: UnboundedReceiver, kernel_message_tx: MessageSender, - // network_error_tx: NetworkErrorSender, - // print_tx: PrintSender, + print_tx: PrintSender, ) -> (NodeId, Option) { println!("maintain_connection\r"); loop { @@ -559,11 +539,22 @@ async fn maintain_connection( maybe_recv = peer_rx.recv() => { match maybe_recv { Some(km) => { - // TODO error handle match send_uqbar_message(&km, &mut conn).await { Ok(()) => continue, Err(e) => { - println!("net: error sending message: {}\r", e); + if e.to_string() == "message too large" { + // this will result in a Timeout if the message + // requested a response, otherwise nothing. so, + // we should always print something to terminal + let _ = print_tx.send(Printout { + verbosity: 0, + content: format!( + "net: tried to send too-large message, limit is {:.2}mb", + MESSAGE_MAX_SIZE as f64 / 1_048_576.0 + ), + }).await; + return (peer.identity.name.clone(), None) + } return (peer.identity.name.clone(), Some(km)) } } @@ -578,8 +569,7 @@ async fn maintain_connection( } } -/// match the streams -/// TODO optimize performance of this +/// cross the streams async fn maintain_passthrough(mut conn: PassthroughConnection) { println!("maintain_passthrough\r"); loop { @@ -682,9 +672,6 @@ async fn recv_connection( let noise = noise.into_transport_mode()?; println!("handshake complete, noise session received\r"); - // TODO if their handshake indicates they want us to proxy - // for them (aka act as a router for them) we can choose - // whether to do so here. Ok(( their_id.clone(), their_handshake.proxy_request, @@ -929,7 +916,6 @@ async fn handle_local_message( // someone wants to open a passthrough with us through a router! // if we are an indirect node, and source is one of our routers, // respond by attempting to init a matching passthrough. - // TODO can discriminate more here.. if our.allowed_routers.contains(&km.source.node) { let Ok((peer_id, peer_conn)) = time::timeout( TIMEOUT, @@ -961,6 +947,7 @@ async fn handle_local_message( peer_conn, peer_rx, kernel_message_tx.clone(), + print_tx.clone(), )); } else { kernel_message_tx diff --git a/src/net/types.rs b/src/net/types.rs index 4141e718..6a7737c1 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -72,7 +72,7 @@ pub struct PendingPassthroughConnection { // TODO upgrade from hashmaps pub type Peers = HashMap>; -pub type PKINames = HashMap; // TODO maybe U256 to String +pub type PKINames = HashMap; pub type OnchainPKI = HashMap; pub type PendingPassthroughs = HashMap<(NodeId, NodeId), PendingPassthroughConnection>; diff --git a/src/net/utils.rs b/src/net/utils.rs index 3b412ad6..25c69f52 100644 --- a/src/net/utils.rs +++ b/src/net/utils.rs @@ -151,7 +151,7 @@ pub fn validate_handshake( pub async fn send_uqbar_message(km: &KernelMessage, conn: &mut PeerConnection) -> Result<()> { let serialized = bincode::serialize(km)?; if serialized.len() > MESSAGE_MAX_SIZE as usize { - return Err(anyhow!("uqbar message too large")); + return Err(anyhow!("message too large")); } let len = (serialized.len() as u32).to_be_bytes(); From 4f1704dcc5539c9fd6c0c7b61b9dc4e68b952333 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Tue, 31 Oct 2023 16:42:46 -0400 Subject: [PATCH 18/40] switch networking protocol to messagepack! --- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 1 + modules/qns_indexer/Cargo.lock | 23 +++++++++++++++++++++++ src/net/mod.rs | 33 +++++++++++++++++++++++++++------ src/net/types.rs | 2 +- src/net/utils.rs | 12 ++++++------ 6 files changed, 81 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 711addd3..43b826d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3762,6 +3762,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "route-recognizer" version = "0.3.1" @@ -5074,6 +5096,7 @@ dependencies = [ "rand", "reqwest", "ring", + "rmp-serde", "route-recognizer", "rsa", "rusoto_core", diff --git a/Cargo.toml b/Cargo.toml index 6f38ebbd..8ac2ecb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ public-ip = "0.2.2" rand = "0.8.4" reqwest = "0.11.18" ring = "0.16.20" +rmp-serde = "1.1.2" route-recognizer = "0.3.1" rsa = "0.9" rusoto_core = "0.48.0" diff --git a/modules/qns_indexer/Cargo.lock b/modules/qns_indexer/Cargo.lock index 1e1724b7..dac49915 100644 --- a/modules/qns_indexer/Cargo.lock +++ b/modules/qns_indexer/Cargo.lock @@ -453,6 +453,7 @@ dependencies = [ "bincode", "cargo-component-bindings", "hex", + "rmp-serde", "serde", "serde_json", "thiserror", @@ -528,6 +529,28 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "ruint" version = "1.10.1" diff --git a/src/net/mod.rs b/src/net/mod.rs index 36563344..436fe75f 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -708,7 +708,7 @@ async fn recv_connection_via_router( let (mut write_stream, mut read_stream) = websocket.split(); // before beginning XX handshake pattern, send a routing request - let message = bincode::serialize(&RoutingRequest { + let message = rmp_serde::to_vec(&RoutingRequest { source: our.name.clone(), signature: keypair .sign([their_name, router.name.as_str()].concat().as_bytes()) @@ -810,7 +810,7 @@ async fn init_connection( // if this is a routed request, before starting XX handshake pattern, send a // routing request message over socket if use_router.is_some() { - let message = bincode::serialize(&RoutingRequest { + let message = rmp_serde::to_vec(&RoutingRequest { source: our.name.clone(), signature: keypair .sign( @@ -890,8 +890,8 @@ async fn handle_local_message( Message::Response((response, _context)) => { // these are received as a router, when we send ConnectionRequests // to a node we do routing for. - match serde_json::from_slice::(&response.ipc)? { - NetResponses::Attempting(_) => { + match rmp_serde::from_slice::(&response.ipc)? { + NetResponses::Accepted(_) => { // TODO anything here? } NetResponses::Rejected(to) => { @@ -907,7 +907,7 @@ async fn handle_local_message( }; if km.source.node != our.name { - if let Ok(act) = serde_json::from_slice::(&ipc) { + if let Ok(act) = rmp_serde::from_slice::(&ipc) { match act { NetActions::QnsBatchUpdate(_) | NetActions::QnsUpdate(_) => { // for now, we don't get these from remote. @@ -949,6 +949,27 @@ async fn handle_local_message( kernel_message_tx.clone(), print_tx.clone(), )); + kernel_message_tx + .send(KernelMessage { + id: km.id, + source: Address { + node: our.name.clone(), + process: ProcessId::from_str("net:sys:uqbar").unwrap(), + }, + target: km.rsvp.unwrap_or(km.source), + rsvp: None, + message: Message::Response(( + Response { + inherit: false, + ipc: rmp_serde::to_vec(&NetResponses::Accepted(from))?, + metadata: None, + }, + None, + )), + payload: None, + signed_capabilities: None, + }) + .await?; } else { kernel_message_tx .send(KernelMessage { @@ -962,7 +983,7 @@ async fn handle_local_message( message: Message::Response(( Response { inherit: false, - ipc: serde_json::to_vec(&NetResponses::Rejected(from))?, + ipc: rmp_serde::to_vec(&NetResponses::Rejected(from))?, metadata: None, }, None, diff --git a/src/net/types.rs b/src/net/types.rs index 6a7737c1..bce52e09 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -99,7 +99,7 @@ pub enum NetActions { /// For now, only sent in response to a ConnectionRequest #[derive(Clone, Debug, Serialize, Deserialize)] pub enum NetResponses { - Attempting(NodeId), + Accepted(NodeId), Rejected(NodeId), } diff --git a/src/net/utils.rs b/src/net/utils.rs index 25c69f52..8f33ffa0 100644 --- a/src/net/utils.rs +++ b/src/net/utils.rs @@ -68,7 +68,7 @@ pub async fn create_passthrough( message: Message::Request(Request { inherit: false, expects_response: Some(5), - ipc: serde_json::to_vec(&NetActions::ConnectionRequest(from_id.name.clone()))?, + ipc: rmp_serde::to_vec(&NetActions::ConnectionRequest(from_id.name.clone()))?, metadata: None, }), payload: None, @@ -111,7 +111,7 @@ pub fn validate_routing_request( pki: &OnchainPKI, ) -> Result<(Identity, NodeId)> { println!("validate_routing_request\r"); - let routing_request: RoutingRequest = bincode::deserialize(buf)?; + let routing_request: RoutingRequest = rmp_serde::from_slice(buf)?; println!("routing request: {:?}\r", routing_request); let their_id = pki .get(&routing_request.source) @@ -149,7 +149,7 @@ pub fn validate_handshake( } pub async fn send_uqbar_message(km: &KernelMessage, conn: &mut PeerConnection) -> Result<()> { - let serialized = bincode::serialize(km)?; + let serialized = rmp_serde::to_vec(km)?; if serialized.len() > MESSAGE_MAX_SIZE as usize { return Err(anyhow!("message too large")); } @@ -186,7 +186,7 @@ pub async fn recv_uqbar_message(conn: &mut PeerConnection) -> Result Result<()> { println!("send_uqbar_handshake\r"); - let our_hs = bincode::serialize(&HandshakePayload { + let our_hs = rmp_serde::to_vec(&HandshakePayload { name: our.name.clone(), signature: keypair.sign(noise_static_key).as_ref().to_vec(), protocol_version: 1, @@ -226,7 +226,7 @@ pub async fn recv_uqbar_handshake( // 2. a signature by their published networking key that signs the // static key they will be using for this handshake // 3. the version number of the networking protocol (so we can upgrade it) - Ok(bincode::deserialize(&buf[..len])?) + Ok(rmp_serde::from_slice(&buf[..len])?) } pub async fn ws_recv( From 64a192b6f37e6b075924adcaa281f431c35b370b Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Wed, 1 Nov 2023 12:10:15 -0400 Subject: [PATCH 19/40] refactoring for clarity --- src/net/mod.rs | 549 +++++++++++++++-------------------------------- src/net/types.rs | 8 +- src/net/utils.rs | 163 ++++++++++++-- 3 files changed, 322 insertions(+), 398 deletions(-) diff --git a/src/net/mod.rs b/src/net/mod.rs index 436fe75f..916c8c2a 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -9,10 +9,12 @@ use std::{ sync::Arc, }; use tokio::net::TcpListener; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; +use tokio::sync::{mpsc::unbounded_channel, RwLock}; use tokio::task::JoinSet; use tokio::time; -use tokio_tungstenite::{accept_async, connect_async, MaybeTlsStream, WebSocketStream}; +use tokio_tungstenite::{ + accept_async, connect_async, tungstenite, MaybeTlsStream, WebSocketStream, +}; mod types; mod utils; @@ -147,7 +149,7 @@ async fn indirect_networking( // if the message is for a peer we currently have a connection with, // try to send it to them else if let Some(peer) = peers.get_mut(target) { - peer.sender.send(km)?; + peer.write().await.sender.send(km)?; } else if let Some(peer_id) = pki.get(target) { // if the message is for a *direct* peer we don't have a connection with, @@ -156,22 +158,17 @@ async fn indirect_networking( // networking information about ourselves to the target. if peer_id.ws_routing.is_some() && reveal_ip { match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id.clone(), - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer_name, peer.clone()); - peer.sender.send(km)?; - peer_connections.spawn(maintain_connection( - peer, + Ok(direct_conn) => { + save_new_peer( + peer_id, + false, + &mut peers, + &mut peer_connections, direct_conn, - peer_rx, - kernel_message_tx.clone(), - print_tx.clone(), - )); + Some(km), + &kernel_message_tx, + &print_tx, + ).await?; } Err(e) => { println!("net: error initializing connection: {}\r", e); @@ -237,23 +234,19 @@ async fn indirect_networking( continue; }; match init_connection(&our, &our_ip, router_id, &keypair, None, true).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: router_id.clone(), - routing_for: false, - sender: peer_tx, - }); - println!("net: connected to router {}\r", peer_name); - peers.insert(peer_name.clone(), peer.clone()); - active_routers.insert(peer_name); - peer_connections.spawn(maintain_connection( - peer, + Ok(direct_conn) => { + println!("net: connected to router {}\r", router_id.name); + active_routers.insert(router_id.name.clone()); + save_new_peer( + router_id, + false, + &mut peers, + &mut peer_connections, direct_conn, - peer_rx, - kernel_message_tx.clone(), - print_tx.clone(), - )); + None, + &kernel_message_tx, + &print_tx, + ).await?; } Err(_e) => continue, } @@ -322,29 +315,24 @@ async fn direct_networking( // if the message is for a peer we currently have a connection with, // try to send it to them else if let Some(peer) = peers.get_mut(target) { - peer.sender.send(km)?; + peer.write().await.sender.send(km)?; } else if let Some(peer_id) = pki.get(target) { // if the message is for a *direct* peer we don't have a connection with, // try to establish a connection with them if peer_id.ws_routing.is_some() { match init_connection(&our, &our_ip, peer_id, &keypair, None, false).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id.clone(), - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer_name, peer.clone()); - peer.sender.send(km)?; - peer_connections.spawn(maintain_connection( - peer, + Ok(direct_conn) => { + save_new_peer( + peer_id, + false, + &mut peers, + &mut peer_connections, direct_conn, - peer_rx, - kernel_message_tx.clone(), - print_tx.clone(), - )); + Some(km), + &kernel_message_tx, + &print_tx, + ).await?; } Err(e) => { println!("net: error initializing connection: {}\r", e); @@ -414,12 +402,12 @@ async fn direct_networking( match conn { Connection::Peer(peer_conn) => { let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { + let peer = Arc::new(RwLock::new(Peer { identity: peer_id, routing_for, sender: peer_tx, - }); - peers.insert(peer.identity.name.clone(), peer.clone()); + })); + peers.insert(peer.read().await.identity.name.clone(), peer.clone()); peer_connections.spawn(maintain_connection( peer, peer_conn, @@ -487,23 +475,19 @@ async fn init_connection_via_router( Some(id) => id, }; match init_connection(&our, &our_ip, peer_id, &keypair, Some(router_id), false).await { - Ok((peer_name, direct_conn)) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id.clone(), - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer_name, peer.clone()); - peer.sender.send(km).unwrap(); - peer_connections.spawn(maintain_connection( - peer, + Ok(direct_conn) => { + return save_new_peer( + peer_id, + false, + peers, + peer_connections, direct_conn, - peer_rx, - kernel_message_tx.clone(), - print_tx.clone(), - )); - return true; + Some(km), + &kernel_message_tx, + &print_tx, + ) + .await + .is_ok() } Err(_) => continue, } @@ -511,95 +495,6 @@ async fn init_connection_via_router( return false; } -async fn maintain_connection( - peer: Arc, - mut conn: PeerConnection, - mut peer_rx: UnboundedReceiver, - kernel_message_tx: MessageSender, - print_tx: PrintSender, -) -> (NodeId, Option) { - println!("maintain_connection\r"); - loop { - tokio::select! { - recv_result = recv_uqbar_message(&mut conn) => { - match recv_result { - Ok(km) => { - if km.source.node != peer.identity.name { - println!("net: got message with spoofed source\r"); - return (peer.identity.name.clone(), None) - } - kernel_message_tx.send(km).await.expect("net error: fatal: kernel died"); - } - Err(e) => { - println!("net: error receiving message: {}\r", e); - return (peer.identity.name.clone(), None) - } - } - }, - maybe_recv = peer_rx.recv() => { - match maybe_recv { - Some(km) => { - match send_uqbar_message(&km, &mut conn).await { - Ok(()) => continue, - Err(e) => { - if e.to_string() == "message too large" { - // this will result in a Timeout if the message - // requested a response, otherwise nothing. so, - // we should always print something to terminal - let _ = print_tx.send(Printout { - verbosity: 0, - content: format!( - "net: tried to send too-large message, limit is {:.2}mb", - MESSAGE_MAX_SIZE as f64 / 1_048_576.0 - ), - }).await; - return (peer.identity.name.clone(), None) - } - return (peer.identity.name.clone(), Some(km)) - } - } - } - None => { - println!("net: peer disconnected\r"); - return (peer.identity.name.clone(), None) - } - } - }, - } - } -} - -/// cross the streams -async fn maintain_passthrough(mut conn: PassthroughConnection) { - println!("maintain_passthrough\r"); - loop { - tokio::select! { - maybe_recv = conn.read_stream_1.next() => { - match maybe_recv { - Some(Ok(msg)) => { - conn.write_stream_2.send(msg).await.expect("net error: fatal: kernel died"); - } - _ => { - println!("net: passthrough broke\r"); - return - } - } - }, - maybe_recv = conn.read_stream_2.next() => { - match maybe_recv { - Some(Ok(msg)) => { - conn.write_stream_1.send(msg).await.expect("net error: fatal: kernel died"); - } - _ => { - println!("net: passthrough broke\r"); - return - } - } - }, - } - } -} - async fn recv_connection( our: &Identity, our_ip: &str, @@ -708,7 +603,7 @@ async fn recv_connection_via_router( let (mut write_stream, mut read_stream) = websocket.split(); // before beginning XX handshake pattern, send a routing request - let message = rmp_serde::to_vec(&RoutingRequest { + let req = rmp_serde::to_vec(&RoutingRequest { source: our.name.clone(), signature: keypair .sign([their_name, router.name.as_str()].concat().as_bytes()) @@ -717,8 +612,7 @@ async fn recv_connection_via_router( target: their_name.to_string(), protocol_version: 1, })?; - ws_send(&mut write_stream, &message).await?; - + write_stream.send(tungstenite::Message::binary(req)).await?; // <- e noise.read_message(&ws_recv(&mut read_stream).await?, &mut buf)?; @@ -771,46 +665,31 @@ async fn init_connection( keypair: &Ed25519KeyPair, use_router: Option<&Identity>, proxy_request: bool, -) -> Result<(String, PeerConnection)> { +) -> Result { println!("init_connection\r"); let mut buf = vec![0u8; 65535]; let (mut noise, our_static_key) = build_initiator(); - let (mut write_stream, mut read_stream) = match use_router { - None => { - let Some((ref ip, ref port)) = peer_id.ws_routing else { - return Err(anyhow!("target has no routing information")); - }; - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; - let Ok(Ok((websocket, _response))) = - time::timeout(TIMEOUT, connect_async(ws_url)).await - else { - return Err(anyhow!("failed to connect to target")); - }; - websocket.split() - } - Some(router_id) => { - let Some((ref ip, ref port)) = router_id.ws_routing else { - return Err(anyhow!("router has no routing information")); - }; - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; - let Ok(Ok((websocket, _response))) = - time::timeout(TIMEOUT, connect_async(ws_url)).await - else { - return Err(anyhow!("failed to connect to target")); - }; - websocket.split() - } + let (ref ip, ref port) = match use_router { + None => peer_id + .ws_routing + .as_ref() + .ok_or(anyhow!("target has no routing information"))?, + Some(router_id) => router_id + .ws_routing + .as_ref() + .ok_or(anyhow!("target has no routing information"))?, }; + let ws_url = make_ws_url(our_ip, ip, port)?; + let Ok(Ok((websocket, _response))) = time::timeout(TIMEOUT, connect_async(ws_url)).await else { + return Err(anyhow!("failed to connect to target")); + }; + let (mut write_stream, mut read_stream) = websocket.split(); // if this is a routed request, before starting XX handshake pattern, send a // routing request message over socket if use_router.is_some() { - let message = rmp_serde::to_vec(&RoutingRequest { + let req = rmp_serde::to_vec(&RoutingRequest { source: our.name.clone(), signature: keypair .sign( @@ -823,12 +702,14 @@ async fn init_connection( target: peer_id.name.clone(), protocol_version: 1, })?; - ws_send(&mut write_stream, &message).await?; + write_stream.send(tungstenite::Message::binary(req)).await?; } // -> e let len = noise.write_message(&[], &mut buf)?; - ws_send(&mut write_stream, &buf[..len]).await?; + write_stream + .send(tungstenite::Message::binary(&buf[..len])) + .await?; // <- e, ee, s, es let their_handshake = recv_uqbar_handshake(&mut noise, &mut buf, &mut read_stream).await?; @@ -857,15 +738,12 @@ async fn init_connection( let noise = noise.into_transport_mode()?; println!("handshake complete, noise session initiated\r"); - Ok(( - their_handshake.name, - PeerConnection { - noise, - buf, - write_stream, - read_stream, - }, - )) + Ok(PeerConnection { + noise, + buf, + write_stream, + read_stream, + }) } /// net module only handles incoming local requests, will never return a response @@ -890,17 +768,20 @@ async fn handle_local_message( Message::Response((response, _context)) => { // these are received as a router, when we send ConnectionRequests // to a node we do routing for. - match rmp_serde::from_slice::(&response.ipc)? { - NetResponses::Accepted(_) => { + match rmp_serde::from_slice::(&response.ipc) { + Ok(NetResponses::Accepted(_)) => { // TODO anything here? } - NetResponses::Rejected(to) => { + Ok(NetResponses::Rejected(to)) => { // drop from our pending map // this will drop the socket, causing initiator to see it as failed pending_passthroughs .ok_or(anyhow!("got net response as non-router"))? .remove(&(to, km.source.node)); } + Err(_) => { + // this is usually the "delivered" response to a raw message + } } return Ok(()); } @@ -916,83 +797,60 @@ async fn handle_local_message( // someone wants to open a passthrough with us through a router! // if we are an indirect node, and source is one of our routers, // respond by attempting to init a matching passthrough. - if our.allowed_routers.contains(&km.source.node) { - let Ok((peer_id, peer_conn)) = time::timeout( + let res: Result = if our.allowed_routers.contains(&km.source.node) + { + let router_id = peers + .get(&km.source.node) + .ok_or(anyhow!("unknown router"))? + .read() + .await + .identity + .clone(); + let (peer_id, peer_conn) = time::timeout( TIMEOUT, recv_connection_via_router( - our, - our_ip, - &from, - pki, - keypair, - &peers - .get(&km.source.node) - .ok_or(anyhow!("unknown router"))? - .identity, + our, our_ip, &from, pki, keypair, &router_id, ), ) - .await? - else { - return Err(anyhow!("someone tried to connect to us but it timed out")); - }; - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(Peer { - identity: peer_id, - routing_for: false, - sender: peer_tx, - }); - peers.insert(peer.identity.name.clone(), peer.clone()); - peer_connections.spawn(maintain_connection( - peer, + .await??; + save_new_peer( + &peer_id, + false, + peers, + peer_connections, peer_conn, - peer_rx, - kernel_message_tx.clone(), - print_tx.clone(), - )); - kernel_message_tx - .send(KernelMessage { - id: km.id, - source: Address { - node: our.name.clone(), - process: ProcessId::from_str("net:sys:uqbar").unwrap(), - }, - target: km.rsvp.unwrap_or(km.source), - rsvp: None, - message: Message::Response(( - Response { - inherit: false, - ipc: rmp_serde::to_vec(&NetResponses::Accepted(from))?, - metadata: None, - }, - None, - )), - payload: None, - signed_capabilities: None, - }) - .await?; + None, + &kernel_message_tx, + &print_tx, + ) + .await?; + Ok(NetResponses::Accepted(from.clone())) } else { - kernel_message_tx - .send(KernelMessage { - id: km.id, - source: Address { - node: our.name.clone(), - process: ProcessId::from_str("net:sys:uqbar").unwrap(), + Ok(NetResponses::Rejected(from.clone())) + }; + kernel_message_tx + .send(KernelMessage { + id: km.id, + source: Address { + node: our.name.clone(), + process: ProcessId::from_str("net:sys:uqbar").unwrap(), + }, + target: km.rsvp.unwrap_or(km.source), + rsvp: None, + message: Message::Response(( + Response { + inherit: false, + ipc: rmp_serde::to_vec( + &res.unwrap_or(NetResponses::Rejected(from)), + )?, + metadata: None, }, - target: km.rsvp.unwrap_or(km.source), - rsvp: None, - message: Message::Response(( - Response { - inherit: false, - ipc: rmp_serde::to_vec(&NetResponses::Rejected(from))?, - metadata: None, - }, - None, - )), - payload: None, - signed_capabilities: None, - }) - .await?; - } + None, + )), + payload: None, + signed_capabilities: None, + }) + .await?; } } return Ok(()); @@ -1034,115 +892,55 @@ async fn handle_local_message( } else { // available commands: "peers", "pki", "names", "diagnostics" // first parse as raw string, then deserialize to NetActions object + let mut printout = String::new(); match std::str::from_utf8(&ipc) { Ok("peers") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("{:#?}", peers.keys()), - }) - .await?; + printout.push_str(&format!("{:#?}", peers.keys())); } Ok("pki") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("{:#?}", pki), - }) - .await?; + printout.push_str(&format!("{:#?}", pki)); } Ok("names") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("{:#?}", names), - }) - .await?; + printout.push_str(&format!("{:#?}", names)); } Ok("diagnostics") => { - print_tx - .send(Printout { - verbosity: 0, - content: format!("our Identity: {:#?}", our), - }) - .await?; - print_tx - .send(Printout { - verbosity: 0, - content: format!("we have connections with peers: {:#?}", peers.keys()), - }) - .await?; - print_tx - .send(Printout { - verbosity: 0, - content: format!("we have {} entries in the PKI", pki.len()), - }) - .await?; - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} open peer connections", - peer_connections.len() - ), - }) - .await?; + printout.push_str(&format!("our Identity: {:#?}\r\n", our)); + printout.push_str(&format!( + "we have connections with peers: {:#?}\r\n", + peers.keys() + )); + printout.push_str(&format!("we have {} entries in the PKI\r\n", pki.len())); + printout.push_str(&format!( + "we have {} open peer connections\r\n", + peer_connections.len() + )); + if pending_passthroughs.is_some() { - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} pending passthrough connections", - pending_passthroughs.unwrap().len() - ), - }) - .await?; + printout.push_str(&format!( + "we have {} pending passthrough connections\r\n", + pending_passthroughs.unwrap().len() + )); } if forwarding_connections.is_some() { - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} open passthrough connections", - forwarding_connections.unwrap().len() - ), - }) - .await?; + printout.push_str(&format!( + "we have {} open passthrough connections\r\n", + forwarding_connections.unwrap().len() + )); } if active_routers.is_some() { - print_tx - .send(Printout { - verbosity: 0, - content: format!( - "we have {} active routers", - active_routers.unwrap().len() - ), - }) - .await?; + printout.push_str(&format!( + "we have {} active routers\r\n", + active_routers.unwrap().len() + )); } } _ => { - let Ok(act) = serde_json::from_slice::(&ipc) else { - print_tx - .send(Printout { - verbosity: 0, - content: "net: got unknown command".into(), - }) - .await?; - return Ok(()); - }; - match act { + match rmp_serde::from_slice::(&ipc)? { NetActions::ConnectionRequest(_) => { // we shouldn't receive these from ourselves. } NetActions::QnsUpdate(log) => { - print_tx - .send(Printout { - verbosity: 1, - content: format!("net: got QNS update for {}", log.name), - }) - .await?; - + // printout.push_str(&format!("net: got QNS update for {}", log.name)); pki.insert( log.name.clone(), Identity { @@ -1159,15 +957,10 @@ async fn handle_local_message( names.insert(log.node, log.name); } NetActions::QnsBatchUpdate(log_list) => { - print_tx - .send(Printout { - verbosity: 1, - content: format!( - "net: got QNS update with {} peers", - log_list.len() - ), - }) - .await?; + // printout.push_str(&format!( + // "net: got QNS update with {} peers", + // log_list.len() + // )); for log in log_list { pki.insert( log.name.clone(), @@ -1189,6 +982,14 @@ async fn handle_local_message( } } } + if !printout.is_empty() { + print_tx + .send(Printout { + verbosity: 0, + content: printout, + }) + .await?; + } Ok(()) } } diff --git a/src/net/types.rs b/src/net/types.rs index bce52e09..dcbdc5d1 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -3,7 +3,7 @@ use futures::stream::{SplitSink, SplitStream}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, sync::Arc}; use tokio::net::TcpStream; -use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::{mpsc::UnboundedSender, RwLock}; use tokio_tungstenite::{tungstenite, MaybeTlsStream, WebSocketStream}; /// Sent to a node when you want to connect directly to them. @@ -71,7 +71,7 @@ pub struct PendingPassthroughConnection { } // TODO upgrade from hashmaps -pub type Peers = HashMap>; +pub type Peers = HashMap>>; pub type PKINames = HashMap; pub type OnchainPKI = HashMap; pub type PendingPassthroughs = HashMap<(NodeId, NodeId), PendingPassthroughConnection>; @@ -84,6 +84,7 @@ pub struct Peer { pub sender: UnboundedSender, } +/// Must be parsed from message pack vector. #[derive(Clone, Debug, Serialize, Deserialize)] pub enum NetActions { /// Received from a router of ours when they have a new pending passthrough for us. @@ -96,7 +97,8 @@ pub enum NetActions { QnsBatchUpdate(Vec), } -/// For now, only sent in response to a ConnectionRequest +/// For now, only sent in response to a ConnectionRequest. +/// Must be parsed from message pack vector #[derive(Clone, Debug, Serialize, Deserialize)] pub enum NetResponses { Accepted(NodeId), diff --git a/src/net/utils.rs b/src/net/utils.rs index 8f33ffa0..ebf67e89 100644 --- a/src/net/utils.rs +++ b/src/net/utils.rs @@ -5,7 +5,13 @@ use futures::stream::{SplitSink, SplitStream}; use futures::{SinkExt, StreamExt}; use ring::signature::{self, Ed25519KeyPair}; use snow::params::NoiseParams; +use std::sync::Arc; use tokio::net::TcpStream; +use tokio::sync::{ + mpsc::{unbounded_channel, UnboundedReceiver}, + RwLock, +}; +use tokio::task::JoinSet; use tokio::time::timeout; use tokio_tungstenite::{connect_async, tungstenite, MaybeTlsStream, WebSocketStream}; @@ -15,6 +21,128 @@ lazy_static::lazy_static! { .expect("net: couldn't build noise params?"); } +pub async fn save_new_peer( + identity: &Identity, + routing_for: bool, + peers: &mut Peers, + peer_connections: &mut JoinSet<(String, Option)>, + conn: PeerConnection, + km: Option, + kernel_message_tx: &MessageSender, + print_tx: &PrintSender, +) -> Result<()> { + println!("save_new_peer\r"); + let peer_name = identity.name.clone(); + let (peer_tx, peer_rx) = unbounded_channel::(); + if km.is_some() { + peer_tx.send(km.unwrap())? + } + let peer = Arc::new(RwLock::new(Peer { + identity: identity.clone(), + routing_for, + sender: peer_tx, + })); + peers.insert(peer_name, peer.clone()); + peer_connections.spawn(maintain_connection( + peer, + conn, + peer_rx, + kernel_message_tx.clone(), + print_tx.clone(), + )); + Ok(()) +} + +pub async fn maintain_connection( + peer: Arc>, + mut conn: PeerConnection, + mut peer_rx: UnboundedReceiver, + kernel_message_tx: MessageSender, + print_tx: PrintSender, +) -> (NodeId, Option) { + println!("maintain_connection\r"); + let peer_name = peer.read().await.identity.name.clone(); + loop { + tokio::select! { + recv_result = recv_uqbar_message(&mut conn) => { + match recv_result { + Ok(km) => { + if km.source.node != peer_name { + println!("net: got message with spoofed source: {}\r", km); + return (peer_name, None) + } + kernel_message_tx.send(km).await.expect("net error: fatal: kernel died"); + } + Err(e) => { + println!("net: error receiving message: {}\r", e); + return (peer_name, None) + } + } + }, + maybe_recv = peer_rx.recv() => { + match maybe_recv { + Some(km) => { + match send_uqbar_message(&km, &mut conn).await { + Ok(()) => continue, + Err(e) => { + if e.to_string() == "message too large" { + // this will result in a Timeout if the message + // requested a response, otherwise nothing. so, + // we should always print something to terminal + let _ = print_tx.send(Printout { + verbosity: 0, + content: format!( + "net: tried to send too-large message, limit is {:.2}mb", + MESSAGE_MAX_SIZE as f64 / 1_048_576.0 + ), + }).await; + return (peer_name, None) + } + return (peer_name, Some(km)) + } + } + } + None => { + println!("net: peer disconnected\r"); + return (peer_name, None) + } + } + }, + } + } +} + +/// cross the streams +pub async fn maintain_passthrough(mut conn: PassthroughConnection) { + println!("maintain_passthrough\r"); + loop { + tokio::select! { + maybe_recv = conn.read_stream_1.next() => { + match maybe_recv { + Some(Ok(msg)) => { + conn.write_stream_2.send(msg).await.expect("net error: fatal: kernel died"); + } + _ => { + println!("net: passthrough broke\r"); + return + } + } + }, + maybe_recv = conn.read_stream_2.next() => { + match maybe_recv { + Some(Ok(msg)) => { + conn.write_stream_1.send(msg).await.expect("net error: fatal: kernel died"); + } + _ => { + println!("net: passthrough broke\r"); + return + } + } + }, + } + } +} + pub async fn create_passthrough( our: &Identity, our_ip: &str, @@ -49,12 +177,12 @@ pub async fn create_passthrough( let target_peer = peers .get(&to_name) .ok_or(anyhow!("can't route to that indirect node"))?; - if !target_peer.routing_for { + if !target_peer.read().await.routing_for { return Err(anyhow!("we don't route for that indirect node")); } // send their net:sys:uqbar process a message, notifying it to create a *matching* // passthrough request, which we can pair with this pending one. - target_peer.sender.send(KernelMessage { + target_peer.write().await.sender.send(KernelMessage { id: rand::random(), source: Address { node: our.name.clone(), @@ -86,9 +214,7 @@ pub async fn create_passthrough( }; // create passthrough to direct node // - let Ok(ws_url) = make_ws_url(our_ip, ip, port) else { - return Err(anyhow!("failed to parse websocket url")); - }; + let ws_url = make_ws_url(our_ip, ip, port)?; let Ok(Ok((websocket, _response))) = timeout(TIMEOUT, connect_async(ws_url)).await else { return Err(anyhow!("failed to connect to target")); }; @@ -157,11 +283,14 @@ pub async fn send_uqbar_message(km: &KernelMessage, conn: &mut PeerConnection) - let len = (serialized.len() as u32).to_be_bytes(); let with_length_prefix = [len.to_vec(), serialized].concat(); + // 65519 = 65535 - 16 (TAGLEN) for payload in with_length_prefix.chunks(65519) { - // 65535 - 16 (TAGLEN) let len = conn.noise.write_message(payload, &mut conn.buf)?; - ws_send(&mut conn.write_stream, &conn.buf[..len]).await?; + conn.write_stream + .feed(tungstenite::Message::binary(&conn.buf[..len])) + .await?; } + conn.write_stream.flush().await?; Ok(()) } @@ -208,7 +337,9 @@ pub async fn send_uqbar_handshake( .expect("failed to serialize handshake payload"); let len = noise.write_message(&our_hs, buf)?; - ws_send(write_stream, &buf[..len]).await?; + write_stream + .send(tungstenite::Message::binary(&buf[..len])) + .await?; Ok(()) } @@ -238,14 +369,6 @@ pub async fn ws_recv( Ok(bin) } -pub async fn ws_send( - write_stream: &mut SplitSink>, tungstenite::Message>, - msg: &[u8], -) -> Result<()> { - write_stream.send(tungstenite::Message::binary(msg)).await?; - Ok(()) -} - pub fn build_responder() -> (snow::HandshakeState, Vec) { let builder: snow::Builder<'_> = snow::Builder::new(PARAMS.clone()); let keypair = builder @@ -274,14 +397,12 @@ pub fn build_initiator() -> (snow::HandshakeState, Vec) { ) } -pub fn make_ws_url(our_ip: &str, ip: &str, port: &u16) -> Result { +pub fn make_ws_url(our_ip: &str, ip: &str, port: &u16) -> Result { // if we have the same public IP as target, route locally, // otherwise they will appear offline due to loopback stuff let ip = if our_ip == ip { "localhost" } else { ip }; - match url::Url::parse(&format!("ws://{}:{}/ws", ip, port)) { - Ok(v) => Ok(v), - Err(_) => Err(SendErrorKind::Offline), - } + let url = url::Url::parse(&format!("ws://{}:{}/ws", ip, port))?; + Ok(url) } pub async fn error_offline(km: KernelMessage, network_error_tx: &NetworkErrorSender) -> Result<()> { From e76a2f9fdea159372e32789ae2607b5688522bea Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Wed, 1 Nov 2023 12:28:40 -0400 Subject: [PATCH 20/40] registre --- src/register | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/register b/src/register index be610be7..81654a0e 160000 --- a/src/register +++ b/src/register @@ -1 +1 @@ -Subproject commit be610be7e3a11d9dfa67b3050ed5e9e62f7e0820 +Subproject commit 81654a0ed724a62748e49feb0c97fad50f1e243a From b0a6f354a7488665871f9ea26b2917dfc5e02ed9 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Wed, 1 Nov 2023 14:52:42 -0400 Subject: [PATCH 21/40] remove unused deps --- Cargo.lock | 34 ---------------------------------- Cargo.toml | 3 --- 2 files changed, 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43b826d8..0039b10f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,17 +189,6 @@ dependencies = [ "term", ] -[[package]] -name = "async-recursion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.32", -] - [[package]] name = "async-trait" version = "0.1.73" @@ -627,17 +616,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cita_trie" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c3d2abadaa28e0d277f9f6d07a2052544f045d929cd4d6f7bcfb43567c9767" -dependencies = [ - "hasher", - "parking_lot", - "rlp", -] - [[package]] name = "coins-bip32" version = "0.8.7" @@ -2106,15 +2084,6 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -[[package]] -name = "hasher" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbba678b6567f27ce22870d951f4208e1dc2fef64993bd4521b1d497ef8a3aa" -dependencies = [ - "tiny-keccak", -] - [[package]] name = "hashers" version = "1.0.1" @@ -5062,7 +5031,6 @@ version = "0.1.0" dependencies = [ "aes-gcm 0.10.2", "anyhow", - "async-recursion", "async-trait", "base64 0.13.1", "bincode", @@ -5071,7 +5039,6 @@ dependencies = [ "cap-std", "chacha20poly1305 0.10.1", "chrono", - "cita_trie", "crossterm", "digest 0.10.7", "dotenv", @@ -5082,7 +5049,6 @@ dependencies = [ "futures", "generic-array", "getrandom", - "hasher", "hex", "hkdf", "hmac 0.12.1", diff --git a/Cargo.toml b/Cargo.toml index 8ac2ecb5..2af2243b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ zip = "0.6" [dependencies] aes-gcm = "0.10.2" anyhow = "1.0.71" -async-recursion = "1.0.4" async-trait = "0.1.71" base64 = "0.13" bincode = "1.3.3" @@ -23,7 +22,6 @@ bytes = "1.4.0" cap-std = "2.0.0" chacha20poly1305 = "0.10.1" chrono = "0.4.31" -cita_trie = "4.0.0" crossterm = { version = "0.26.1", features = ["event-stream", "bracketed-paste"] } digest = "0.10" dotenv = "0.15.0" @@ -34,7 +32,6 @@ flate2 = "1.0" futures = "0.3" generic-array = "0.14" getrandom = "0.2.10" -hasher = "*" hex = "0.4.3" hkdf = "0.12.3" hmac = "0.12" From c9aff54255cfd0a9e014bb65cbc2c8c161adeef0 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Wed, 1 Nov 2023 16:37:31 -0400 Subject: [PATCH 22/40] cleanup, remove prints, ready to go --- README.md | 6 +++++- src/net/mod.rs | 54 ++++++++++++++++-------------------------------- src/net/types.rs | 4 ++-- src/net/utils.rs | 49 +++++++++++++------------------------------ 4 files changed, 39 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 0de1952b..a0b3275a 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,8 @@ On boot you will be prompted to navigate to `localhost:8080`. Make sure your eth ## Example usage -TODO +Download and install an app: +``` +!message our main:app_store:uqbar {"Download": {"package": {"package_name": "", "publisher_node": ""}, "install_from": ""}} +!message our main:app_store:uqbar {"Install": {"package_name": "", "publisher_node": ""}} +``` diff --git a/src/net/mod.rs b/src/net/mod.rs index 916c8c2a..5e926123 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -38,8 +38,6 @@ pub async fn networking( message_rx: MessageReceiver, reveal_ip: bool, ) -> Result<()> { - println!("networking!\r"); - println!("our identity: {:#?}\r", our); // branch on whether we are a direct or indirect node match &our.ws_routing { None => { @@ -102,13 +100,12 @@ async fn indirect_networking( mut message_rx: MessageReceiver, reveal_ip: bool, ) -> Result<()> { - println!("indirect_networking\r"); let mut pki: OnchainPKI = HashMap::new(); let mut peers: Peers = HashMap::new(); // mapping from QNS namehash to username let mut names: PKINames = HashMap::new(); - let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); + // indirect-specific structure let mut active_routers = HashSet::::new(); loop { @@ -141,7 +138,7 @@ async fn indirect_networking( Err(e) => { print_tx.send(Printout { verbosity: 0, - content: format!("net: error handling local message: {}", e) + content: format!("net: error handling local message: {e}") }).await?; } } @@ -170,8 +167,7 @@ async fn indirect_networking( &print_tx, ).await?; } - Err(e) => { - println!("net: error initializing connection: {}\r", e); + Err(_) => { error_offline(km, &network_error_tx).await?; } } @@ -198,7 +194,6 @@ async fn indirect_networking( )).await; if !sent.unwrap_or(false) { // none of the routers worked! - println!("net: error initializing routed connection\r"); error_offline(km, &network_error_tx).await?; } } @@ -235,7 +230,10 @@ async fn indirect_networking( }; match init_connection(&our, &our_ip, router_id, &keypair, None, true).await { Ok(direct_conn) => { - println!("net: connected to router {}\r", router_id.name); + print_tx.send(Printout { + verbosity: 0, + content: format!("now connected to router {}", router_id.name), + }).await?; active_routers.insert(router_id.name.clone()); save_new_peer( router_id, @@ -267,13 +265,12 @@ async fn direct_networking( self_message_tx: MessageSender, mut message_rx: MessageReceiver, ) -> Result<()> { - println!("direct_networking\r"); let mut pki: OnchainPKI = HashMap::new(); let mut peers: Peers = HashMap::new(); // mapping from QNS namehash to username let mut names: PKINames = HashMap::new(); - let mut peer_connections = JoinSet::<(NodeId, Option)>::new(); + // direct-specific structures let mut forwarding_connections = JoinSet::<()>::new(); let mut pending_passthroughs: PendingPassthroughs = HashMap::new(); @@ -334,8 +331,7 @@ async fn direct_networking( &print_tx, ).await?; } - Err(e) => { - println!("net: error initializing connection: {}\r", e); + Err(_) => { error_offline(km, &network_error_tx).await?; } } @@ -361,7 +357,6 @@ async fn direct_networking( )).await; if !sent.unwrap_or(false) { // none of the routers worked! - println!("net: error initializing routed connection\r"); error_offline(km, &network_error_tx).await?; } } @@ -390,7 +385,10 @@ async fn direct_networking( { Ok(res) => res, Err(e) => { - println!("net: recv_connection failed: {e}\r"); + print_tx.send(Printout { + verbosity: 0, + content: format!("net: recv_connection failed: {e}"), + }).await?; continue; } }; @@ -460,7 +458,6 @@ async fn init_connection_via_router( kernel_message_tx: MessageSender, print_tx: PrintSender, ) -> bool { - println!("init_connection_via_router\r"); let routers_shuffled = { let mut routers = peer_id.allowed_routers.clone(); routers.shuffle(&mut rand::thread_rng()); @@ -504,7 +501,6 @@ async fn recv_connection( keypair: &Ed25519KeyPair, websocket: WebSocketStream>, ) -> Result<(Identity, bool, Connection)> { - println!("recv_connection\r"); let mut buf = vec![0u8; 65535]; let (mut noise, our_static_key) = build_responder(); let (mut write_stream, mut read_stream) = websocket.split(); @@ -563,15 +559,11 @@ async fn recv_connection( their_id, )?; - // Transition the state machine into transport mode now that the handshake is complete. - let noise = noise.into_transport_mode()?; - println!("handshake complete, noise session received\r"); - Ok(( their_id.clone(), their_handshake.proxy_request, Connection::Peer(PeerConnection { - noise, + noise: noise.into_transport_mode()?, buf, write_stream, read_stream, @@ -587,7 +579,6 @@ async fn recv_connection_via_router( keypair: &Ed25519KeyPair, router: &Identity, ) -> Result<(Identity, PeerConnection)> { - println!("recv_connection_via_router\r"); let mut buf = vec![0u8; 65535]; let (mut noise, our_static_key) = build_responder(); @@ -604,13 +595,13 @@ async fn recv_connection_via_router( // before beginning XX handshake pattern, send a routing request let req = rmp_serde::to_vec(&RoutingRequest { + protocol_version: 1, source: our.name.clone(), signature: keypair .sign([their_name, router.name.as_str()].concat().as_bytes()) .as_ref() .to_vec(), target: their_name.to_string(), - protocol_version: 1, })?; write_stream.send(tungstenite::Message::binary(req)).await?; // <- e @@ -643,14 +634,10 @@ async fn recv_connection_via_router( their_id, )?; - // Transition the state machine into transport mode now that the handshake is complete. - let noise = noise.into_transport_mode()?; - println!("handshake complete, noise session received\r"); - Ok(( their_id.clone(), PeerConnection { - noise, + noise: noise.into_transport_mode()?, buf, write_stream, read_stream, @@ -666,7 +653,6 @@ async fn init_connection( use_router: Option<&Identity>, proxy_request: bool, ) -> Result { - println!("init_connection\r"); let mut buf = vec![0u8; 65535]; let (mut noise, our_static_key) = build_initiator(); @@ -690,6 +676,7 @@ async fn init_connection( // routing request message over socket if use_router.is_some() { let req = rmp_serde::to_vec(&RoutingRequest { + protocol_version: 1, source: our.name.clone(), signature: keypair .sign( @@ -700,7 +687,6 @@ async fn init_connection( .as_ref() .to_vec(), target: peer_id.name.clone(), - protocol_version: 1, })?; write_stream.send(tungstenite::Message::binary(req)).await?; } @@ -735,11 +721,8 @@ async fn init_connection( ) .await?; - let noise = noise.into_transport_mode()?; - println!("handshake complete, noise session initiated\r"); - Ok(PeerConnection { - noise, + noise: noise.into_transport_mode()?, buf, write_stream, read_stream, @@ -762,7 +745,6 @@ async fn handle_local_message( kernel_message_tx: &MessageSender, print_tx: &PrintSender, ) -> Result<()> { - println!("handle_local_message\r"); let ipc = match km.message { Message::Request(request) => request.ipc, Message::Response((response, _context)) => { diff --git a/src/net/types.rs b/src/net/types.rs index dcbdc5d1..9d2ab882 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -10,6 +10,7 @@ use tokio_tungstenite::{tungstenite, MaybeTlsStream, WebSocketStream}; /// Sent in the 'e, ee, s, es' and 's, se' phases of XX noise protocol pattern. #[derive(Debug, Deserialize, Serialize)] pub struct HandshakePayload { + pub protocol_version: u8, pub name: NodeId, // signature is created by their networking key, of their static key // someone could reuse this signature, but then they will be unable @@ -20,7 +21,6 @@ pub struct HandshakePayload { /// including from the router itself. /// This is not relevant in a handshake sent from the receiver side. pub proxy_request: bool, - pub protocol_version: u8, } /// Sent to a node when you want them to connect you to an indirect node. @@ -36,12 +36,12 @@ pub struct HandshakePayload { /// Sent in the 'e' phase of XX noise protocol pattern. #[derive(Debug, Deserialize, Serialize)] pub struct RoutingRequest { + pub protocol_version: u8, pub source: NodeId, // signature is created by their networking key, of the [target, router name].concat() // someone could reuse this signature, and TODO need to make sure that's useless. pub signature: Vec, pub target: NodeId, - pub protocol_version: u8, } pub enum Connection { diff --git a/src/net/utils.rs b/src/net/utils.rs index ebf67e89..66384de3 100644 --- a/src/net/utils.rs +++ b/src/net/utils.rs @@ -31,7 +31,6 @@ pub async fn save_new_peer( kernel_message_tx: &MessageSender, print_tx: &PrintSender, ) -> Result<()> { - println!("save_new_peer\r"); let peer_name = identity.name.clone(); let (peer_tx, peer_rx) = unbounded_channel::(); if km.is_some() { @@ -53,6 +52,7 @@ pub async fn save_new_peer( Ok(()) } +/// should always be spawned on its own task pub async fn maintain_connection( peer: Arc>, mut conn: PeerConnection, @@ -60,7 +60,6 @@ pub async fn maintain_connection( kernel_message_tx: MessageSender, print_tx: PrintSender, ) -> (NodeId, Option) { - println!("maintain_connection\r"); let peer_name = peer.read().await.identity.name.clone(); loop { tokio::select! { @@ -68,13 +67,15 @@ pub async fn maintain_connection( match recv_result { Ok(km) => { if km.source.node != peer_name { - println!("net: got message with spoofed source: {}\r", km); + let _ = print_tx.send(Printout { + verbosity: 0, + content: format!("net: got message with spoofed source from {peer_name}") + }).await; return (peer_name, None) } kernel_message_tx.send(km).await.expect("net error: fatal: kernel died"); } - Err(e) => { - println!("net: error receiving message: {}\r", e); + Err(_) => { return (peer_name, None) } } @@ -102,10 +103,7 @@ pub async fn maintain_connection( } } } - None => { - println!("net: peer disconnected\r"); - return (peer_name, None) - } + None => return (peer_name, None), } }, } @@ -114,7 +112,6 @@ pub async fn maintain_connection( /// cross the streams pub async fn maintain_passthrough(mut conn: PassthroughConnection) { - println!("maintain_passthrough\r"); loop { tokio::select! { maybe_recv = conn.read_stream_1.next() => { @@ -122,10 +119,7 @@ pub async fn maintain_passthrough(mut conn: PassthroughConnection) { Some(Ok(msg)) => { conn.write_stream_2.send(msg).await.expect("net error: fatal: kernel died"); } - _ => { - println!("net: passthrough broke\r"); - return - } + _ => return, } }, maybe_recv = conn.read_stream_2.next() => { @@ -133,10 +127,7 @@ pub async fn maintain_passthrough(mut conn: PassthroughConnection) { Some(Ok(msg)) => { conn.write_stream_1.send(msg).await.expect("net error: fatal: kernel died"); } - _ => { - println!("net: passthrough broke\r"); - return - } + _ => return, } }, } @@ -154,11 +145,8 @@ pub async fn create_passthrough( write_stream_1: SplitSink>, tungstenite::Message>, read_stream_1: SplitStream>>, ) -> Result<(Identity, Connection)> { - println!("create_passthrough\r"); // if the target has already generated a pending passthrough for this source, // immediately match them - println!("current: {:?}\r", pending_passthroughs.keys()); - println!("this one: {:?}\r", (to_name.clone(), from_id.name.clone())); if let Some(pending) = pending_passthroughs.remove(&(to_name.clone(), from_id.name.clone())) { return Ok(( from_id, @@ -219,7 +207,6 @@ pub async fn create_passthrough( return Err(anyhow!("failed to connect to target")); }; let (write_stream_2, read_stream_2) = websocket.split(); - Ok(( from_id, Connection::Passthrough(PassthroughConnection { @@ -236,9 +223,7 @@ pub fn validate_routing_request( buf: &[u8], pki: &OnchainPKI, ) -> Result<(Identity, NodeId)> { - println!("validate_routing_request\r"); let routing_request: RoutingRequest = rmp_serde::from_slice(buf)?; - println!("routing request: {:?}\r", routing_request); let their_id = pki .get(&routing_request.source) .ok_or(anyhow!("unknown QNS name"))?; @@ -261,7 +246,6 @@ pub fn validate_handshake( their_static_key: &[u8], their_id: &Identity, ) -> Result<()> { - println!("validate_handshake\r"); if handshake.protocol_version != 1 { return Err(anyhow!("handshake protocol version mismatch")); } @@ -294,6 +278,7 @@ pub async fn send_uqbar_message(km: &KernelMessage, conn: &mut PeerConnection) - Ok(()) } +/// any error in receiving a message will result in the connection being closed. pub async fn recv_uqbar_message(conn: &mut PeerConnection) -> Result { let outer_len = conn .noise @@ -304,6 +289,9 @@ pub async fn recv_uqbar_message(conn: &mut PeerConnection) -> Result MESSAGE_MAX_SIZE { + return Err(anyhow!("message too large")); + } let mut msg = Vec::with_capacity(msg_len as usize); msg.extend_from_slice(&conn.buf[4..outer_len]); @@ -327,11 +315,10 @@ pub async fn send_uqbar_handshake( write_stream: &mut SplitSink>, tungstenite::Message>, proxy_request: bool, ) -> Result<()> { - println!("send_uqbar_handshake\r"); let our_hs = rmp_serde::to_vec(&HandshakePayload { + protocol_version: 1, name: our.name.clone(), signature: keypair.sign(noise_static_key).as_ref().to_vec(), - protocol_version: 1, proxy_request, }) .expect("failed to serialize handshake payload"); @@ -340,7 +327,6 @@ pub async fn send_uqbar_handshake( write_stream .send(tungstenite::Message::binary(&buf[..len])) .await?; - Ok(()) } @@ -349,14 +335,7 @@ pub async fn recv_uqbar_handshake( buf: &mut Vec, read_stream: &mut SplitStream>>, ) -> Result { - println!("recv_uqbar_handshake\r"); let len = noise.read_message(&ws_recv(read_stream).await?, buf)?; - - // from buffer, read a sequence of bytes that deserializes to the - // 1. QNS name of the sender - // 2. a signature by their published networking key that signs the - // static key they will be using for this handshake - // 3. the version number of the networking protocol (so we can upgrade it) Ok(rmp_serde::from_slice(&buf[..len])?) } From 05cdf2188abd46de08a372b58a5afa549f526411 Mon Sep 17 00:00:00 2001 From: dr-frmr <93405247+dr-frmr@users.noreply.github.com> Date: Mon, 6 Nov 2023 10:44:54 -0500 Subject: [PATCH 23/40] re-add async-recursion, whoops --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2af2243b..5df58169 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ zip = "0.6" [dependencies] aes-gcm = "0.10.2" anyhow = "1.0.71" +async-recursion = "1.0.4" async-trait = "0.1.71" base64 = "0.13" bincode = "1.3.3" @@ -65,4 +66,4 @@ uuid = { version = "1.1.2", features = ["serde", "v4"] } warp = "0.3.5" wasmtime = "12.0.1" wasmtime-wasi = "12.0.1" -zip = "0.6" \ No newline at end of file +zip = "0.6" From 545018f8f546704e62b034393ec4c3afad2dd064 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 11:14:50 -0500 Subject: [PATCH 24/40] net: bugfix for router-init --- Cargo.lock | 12 ++++++++++++ src/net/mod.rs | 27 +++++++++++++++------------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0039b10f..0345647a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,6 +189,17 @@ dependencies = [ "term", ] +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "async-trait" version = "0.1.73" @@ -5031,6 +5042,7 @@ version = "0.1.0" dependencies = [ "aes-gcm 0.10.2", "anyhow", + "async-recursion", "async-trait", "base64 0.13.1", "bincode", diff --git a/src/net/mod.rs b/src/net/mod.rs index 5e926123..58d8edda 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -399,20 +399,16 @@ async fn direct_networking( // forwarding connections joinset match conn { Connection::Peer(peer_conn) => { - let (peer_tx, peer_rx) = unbounded_channel::(); - let peer = Arc::new(RwLock::new(Peer { - identity: peer_id, + save_new_peer( + &peer_id, routing_for, - sender: peer_tx, - })); - peers.insert(peer.read().await.identity.name.clone(), peer.clone()); - peer_connections.spawn(maintain_connection( - peer, + &mut peers, + &mut peer_connections, peer_conn, - peer_rx, - kernel_message_tx.clone(), - print_tx.clone(), - )); + None, + &kernel_message_tx, + &print_tx + ).await?; } Connection::Passthrough(passthrough_conn) => { forwarding_connections.spawn(maintain_passthrough( @@ -891,6 +887,13 @@ async fn handle_local_message( "we have connections with peers: {:#?}\r\n", peers.keys() )); + printout.push_str(&format!( + "we are routing for: {:#?}\r\n", + peers + .iter() + .filter(|(_, peer)| peer.read().await.routing_for) + .map(|(id, _)| id) + )); printout.push_str(&format!("we have {} entries in the PKI\r\n", pki.len())); printout.push_str(&format!( "we have {} open peer connections\r\n", From 434702febbafafc1ff8baab35883e9b37177e568 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 11:31:54 -0500 Subject: [PATCH 25/40] net: map -> keys --- src/net/mod.rs | 4 ++-- src/types.rs | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/net/mod.rs b/src/net/mod.rs index 58d8edda..2772ca2b 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -9,7 +9,6 @@ use std::{ sync::Arc, }; use tokio::net::TcpListener; -use tokio::sync::{mpsc::unbounded_channel, RwLock}; use tokio::task::JoinSet; use tokio::time; use tokio_tungstenite::{ @@ -891,8 +890,9 @@ async fn handle_local_message( "we are routing for: {:#?}\r\n", peers .iter() - .filter(|(_, peer)| peer.read().await.routing_for) + .filter(|(_, peer)| peer.blocking_read().routing_for) .map(|(id, _)| id) + .collect::>() )); printout.push_str(&format!("we have {} entries in the PKI\r\n", pki.len())); printout.push_str(&format!( diff --git a/src/types.rs b/src/types.rs index ac748d61..2eb89250 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,12 +1,8 @@ use crate::kernel::uqbar::process::standard as wit; use ring::signature; use serde::{Deserialize, Serialize}; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; +use std::collections::{HashMap, HashSet}; use thiserror::Error; -use tokio::sync::RwLock; lazy_static::lazy_static! { pub static ref ENCRYPTOR_PROCESS_ID: ProcessId = ProcessId::new(Some("encryptor"), "sys", "uqbar"); From 9e18cb56f6a08621dc8863d72fdb28cdb266f9a7 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 11:53:12 -0500 Subject: [PATCH 26/40] net: back to standard routers --- src/register.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/register.rs b/src/register.rs index 9b55fa68..16146261 100644 --- a/src/register.rs +++ b/src/register.rs @@ -263,11 +263,9 @@ async fn handle_info( name: String::new(), ws_routing: Some((ip.clone(), ws_port)), allowed_routers: vec![ - "testnode101.uq".into(), - "testnode102.uq".into(), - // "uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(), - // "uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(), - // "uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(), + "uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(), + "uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(), + "uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(), ], }; From 5ab1f7c102e694b056db414dc12d4408c26c6a00 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 12:04:13 -0500 Subject: [PATCH 27/40] net: fix for silly async block --- src/net/mod.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/net/mod.rs b/src/net/mod.rs index 2772ca2b..b948fc26 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -882,24 +882,20 @@ async fn handle_local_message( } Ok("diagnostics") => { printout.push_str(&format!("our Identity: {:#?}\r\n", our)); - printout.push_str(&format!( - "we have connections with peers: {:#?}\r\n", - peers.keys() - )); - printout.push_str(&format!( - "we are routing for: {:#?}\r\n", - peers - .iter() - .filter(|(_, peer)| peer.blocking_read().routing_for) - .map(|(id, _)| id) - .collect::>() - )); + printout.push_str(&format!("we have connections with peers:\r\n")); + for peer in peers.values() { + let read = peer.read().await; + printout.push_str(&format!( + "{}, routing_for={}\r\n", + read.identity.name, + read.routing_for, + )); + } printout.push_str(&format!("we have {} entries in the PKI\r\n", pki.len())); printout.push_str(&format!( "we have {} open peer connections\r\n", peer_connections.len() )); - if pending_passthroughs.is_some() { printout.push_str(&format!( "we have {} pending passthrough connections\r\n", From 72542f701707b440056be0ac9c71f52bc69aa09c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:04:43 +0000 Subject: [PATCH 28/40] Format Rust code using rustfmt --- src/net/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/net/mod.rs b/src/net/mod.rs index b948fc26..0b473adc 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -887,8 +887,7 @@ async fn handle_local_message( let read = peer.read().await; printout.push_str(&format!( "{}, routing_for={}\r\n", - read.identity.name, - read.routing_for, + read.identity.name, read.routing_for, )); } printout.push_str(&format!("we have {} entries in the PKI\r\n", pki.len())); From f527b68ab64035f7544a8ee9688ef740b42bc5c9 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 14:26:04 -0500 Subject: [PATCH 29/40] make homepage, qns, term good --- Cargo.lock | 1 - build.rs | 2 +- modules/homepage/Cargo.lock | 252 +++++-------- modules/homepage/Cargo.toml | 11 +- modules/homepage/src/lib.rs | 316 +++++++--------- modules/qns_indexer/Cargo-component.lock | 3 - modules/qns_indexer/Cargo.lock | 459 ++++++----------------- modules/qns_indexer/src/lib.rs | 120 +++--- modules/terminal/Cargo.toml | 3 - process_lib/Cargo.toml | 3 +- process_lib/src/kernel_types.rs | 21 +- process_lib/src/lib.rs | 22 +- 12 files changed, 432 insertions(+), 781 deletions(-) delete mode 100644 modules/qns_indexer/Cargo-component.lock diff --git a/Cargo.lock b/Cargo.lock index 0345647a..33f3c6cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5106,7 +5106,6 @@ dependencies = [ "bincode", "rand", "serde", - "serde_json", "wit-bindgen", ] diff --git a/build.rs b/build.rs index 10f49edb..1366c0fc 100644 --- a/build.rs +++ b/build.rs @@ -153,7 +153,7 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if package_name != "terminal" { + if package_name != "homepage" { continue; } diff --git a/modules/homepage/Cargo.lock b/modules/homepage/Cargo.lock index 1e259f97..ab1f6a96 100644 --- a/modules/homepage/Cargo.lock +++ b/modules/homepage/Cargo.lock @@ -17,12 +17,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" @@ -30,28 +24,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#36c221e41db3e87dec4c82eadcb9bc8f37626533" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#36c221e41db3e87dec4c82eadcb9bc8f37626533" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "equivalent" @@ -60,19 +36,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "form_urlencoded" -version = "1.2.0" +name = "getrandom" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "percent-encoding", + "cfg-if", + "libc", + "wasi", ] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -89,9 +67,9 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "serde", "serde_json", + "uqbar_process_lib", "wit-bindgen", ] @@ -101,21 +79,11 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", @@ -134,6 +102,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + [[package]] name = "log" version = "0.4.20" @@ -141,16 +115,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] -name = "memchr" -version = "2.6.3" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" @@ -161,17 +129,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "quote" version = "1.0.33" @@ -181,6 +138,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "ryu" version = "1.0.15" @@ -189,9 +176,9 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" @@ -226,9 +213,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "spdx" @@ -250,51 +237,12 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -308,40 +256,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand", + "serde", + "wit-bindgen", ] [[package]] -name = "version_check" -version = "0.9.4" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -350,9 +299,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -360,19 +309,17 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ - "bitflags 2.4.0", + "bitflags", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ "anyhow", "wit-component", @@ -381,54 +328,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -438,16 +373,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/homepage/Cargo.toml b/modules/homepage/Cargo.toml index d9391a55..c04d5a0c 100644 --- a/modules/homepage/Cargo.toml +++ b/modules/homepage/Cargo.toml @@ -13,18 +13,13 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +uqbar_process_lib = { path = "../../process_lib" } [lib] crate-type = ["cdylib"] [package.metadata.component] -package = "component:uq-process" - -[package.metadata.component.target] -path = "wit" - -[package.metadata.component.dependencies] +package = "uqbar:process" diff --git a/modules/homepage/src/lib.rs b/modules/homepage/src/lib.rs index 43e118df..d651f710 100644 --- a/modules/homepage/src/lib.rs +++ b/modules/homepage/src/lib.rs @@ -1,200 +1,170 @@ -cargo_component_bindings::generate!(); - -use bindings::component::uq_process::types::*; -use bindings::{ - get_payload, print_to_terminal, receive, send_request, send_requests, send_response, Guest, -}; use serde_json::json; +use uqbar_process_lib::{get_payload, receive, Address, Message, Payload, Request, Response}; -#[allow(dead_code)] -mod process_lib; +wit_bindgen::generate!({ + path: "../../wit", + world: "process", + exports: { + world: Component, + }, +}); struct Component; const HOME_PAGE: &str = include_str!("home.html"); -fn generate_http_binding( - add: Address, - path: &str, - authenticated: bool, -) -> (Address, Request, Option, Option) { - ( - add, - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": path, - "authenticated": authenticated, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ) +fn serialize_json_message(message: &serde_json::Value) -> anyhow::Result> { + Ok(serde_json::to_vec(message)?) } impl Guest for Component { - fn init(our: Address) { - print_to_terminal(0, "homepage: start"); + fn init(our: String) { + let our = Address::from_str(&our).unwrap(); + println!("homepage: start"); - let bindings_address = Address { - node: our.node.clone(), - process: ProcessId::from_str("http_server:sys:uqbar").unwrap(), + match main(our) { + Ok(_) => {} + Err(e) => { + println!("homepage: ended with error: {:?}", e); + } + } + } +} + +fn main(our: Address) -> anyhow::Result<()> { + // bind to root path on http_server + Request::new() + .target(Address::new(&our.node, "http_server:sys:uqbar")?)? + .ipc( + &json!({ + "BindPath": { + "path": "/", + "authenticated": true, + "local_only": false + } + }), + serialize_json_message, + )? + .send()?; + + loop { + let Ok((_source, message)) = receive() else { + println!("homepage: got network error"); + continue; + }; + let Message::Request(request) = message else { + println!("homepage: got unexpected message: {:?}", message); + continue; }; - // , option> - let http_endpoint_binding_requests: [(Address, Request, Option, Option); - 1] = [generate_http_binding(bindings_address.clone(), "/", true)]; - send_requests(&http_endpoint_binding_requests); - - loop { - let Ok((_source, message)) = receive() else { - print_to_terminal(0, "homepage: got network error"); + let message_json: serde_json::Value = match serde_json::from_slice(&request.ipc) { + Ok(v) => v, + Err(_) => { + println!("homepage: failed to parse ipc JSON, skipping"); continue; - }; - let Message::Request(request) = message else { - print_to_terminal(0, &format!("homepage: got unexpected message: {:?}", message)); - continue; - }; + } + }; - let message_json: serde_json::Value = match serde_json::from_slice(&request.ipc) { - Ok(v) => v, - Err(_) => { - print_to_terminal(1, "homepage: failed to parse ipc JSON, skipping"); - continue; - } - }; - - if message_json["path"] == "/" && message_json["method"] == "GET" { - print_to_terminal(1, "homepage: sending response"); - - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "action": "response", - "status": 200, - "headers": { - "Content-Type": "text/html", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("text/html".to_string()), - bytes: HOME_PAGE - .replace("${our}", &our.node) - .to_string() - .as_bytes() - .to_vec(), + if message_json["path"] == "/" && message_json["method"] == "GET" { + println!("homepage: sending response"); + Response::new() + .ipc( + &json!({ + "action": "response", + "status": 200, + "headers": { + "Content-Type": "text/html", + }, }), - ); - } else if message_json["path"].is_string() { - send_response( - &Response { - inherit: false, - ipc: json!({ - "action": "response", - "status": 404, - "headers": { - "Content-Type": "text/html", - }, - }) + serialize_json_message, + )? + .payload(Payload { + mime: Some("text/html".to_string()), + bytes: HOME_PAGE + .replace("${our}", &our.node) .to_string() .as_bytes() .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("text/html".to_string()), - bytes: "Not Found".to_string().as_bytes().to_vec(), + }) + .send()?; + } else if message_json["path"].is_string() { + Response::new() + .ipc( + &json!({ + "action": "response", + "status": 404, + "headers": { + "Content-Type": "text/html", + }, }), - ); - } else if message_json["hello"] == "world" { - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "hello": "to you too" - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: serde_json::json!({ - "hello": "to you too" - }) - .to_string() - .as_bytes() - .to_vec(), + serialize_json_message, + )? + .payload(Payload { + mime: Some("text/html".to_string()), + bytes: "Not Found".to_string().as_bytes().to_vec(), + }) + .send()?; + } else if message_json["hello"] == "world" { + Response::new() + .ipc( + &json!({ + "hello": "to you too" }), - ); - } else { - if let Some(payload) = get_payload() { - if let Ok(json) = serde_json::from_slice::(&payload.bytes) { - print_to_terminal(1, format!("JSON: {}", json).as_str()); - if json["message"] == "ping" { - // WebSocket pushes are sent as requests - send_request( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("encryptor:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: None, - ipc: serde_json::json!({ - "EncryptAndForwardAction": { - "channel_id": "homepage", - "forward_to": { - "node": our.node.clone(), - "process": { - "process_name": "http_server", - "package_name": "sys", - "publisher_node": "uqbar" + serialize_json_message, + )? + .payload(Payload { + mime: Some("application/json".to_string()), + bytes: serde_json::json!({ + "hello": "to you too" + }) + .to_string() + .as_bytes() + .to_vec(), + }) + .send()?; + } else { + if let Some(payload) = get_payload() { + if let Ok(json) = serde_json::from_slice::(&payload.bytes) { + // println!("JSON: {}", json); + if json["message"] == "ping" { + // WebSocket pushes are sent as requests + Request::new() + .target(Address::new(&our.node, "encryptor:sys:uqbar")?)? + .ipc( + &json!({ + "EncryptAndForwardAction": { + "channel_id": "homepage", + "forward_to": { + "node": our.node.clone(), + "process": { + "process_name": "http_server", + "package_name": "sys", + "publisher_node": "uqbar" + } + }, // node, process + "json": Some(json!({ // this is the JSON to forward + "WebSocketPush": { + "target": { + "node": our.node.clone(), + "id": "homepage", // If the message passed in an ID then we could send to just that ID } - }, // node, process - "json": Some(serde_json::json!({ // this is the JSON to forward - "WebSocketPush": { - "target": { - "node": our.node.clone(), - "id": "homepage", // If the message passed in an ID then we could send to just that ID - } - } - })), - } + } + })), + } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: serde_json::json!({ - "pong": true - }) - .to_string() - .as_bytes() - .to_vec(), }), - ); - } + serialize_json_message, + )? + .payload(Payload { + mime: Some("application/json".to_string()), + bytes: serde_json::json!({ + "pong": true + }) + .to_string() + .as_bytes() + .to_vec(), + }) + .send()?; } } } diff --git a/modules/qns_indexer/Cargo-component.lock b/modules/qns_indexer/Cargo-component.lock deleted file mode 100644 index 00bc239d..00000000 --- a/modules/qns_indexer/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/modules/qns_indexer/Cargo.lock b/modules/qns_indexer/Cargo.lock index dac49915..fc26bb2c 100644 --- a/modules/qns_indexer/Cargo.lock +++ b/modules/qns_indexer/Cargo.lock @@ -23,9 +23,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f938f00332d63a5b0ac687bd6f46d03884638948921d9f8b50c59563d421ae25" +checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" dependencies = [ "arrayvec", "bytes", @@ -42,7 +42,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", "syn-solidity", "tiny-keccak", ] @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayvec" @@ -109,52 +109,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#d14cef65719d0d186218d1dfe5f04bbbf295dc80" -dependencies = [ - "cargo-component-macro", - "wit-bindgen 0.9.0", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#d14cef65719d0d186218d1dfe5f04bbbf295dc80" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component 0.13.1", -] - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cfg-if" @@ -164,13 +133,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "const-hex" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08849ed393c907c90016652a01465a12d86361cd38ad2a7de026c56a520cc259" +checksum = "a5104de16b218eddf8e34ffe2f86f74bfa4e61e95a1b89732fccf6325efd0557" dependencies = [ "cfg-if", "cpufeatures", "hex", + "proptest", "serde", ] @@ -182,9 +152,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -222,30 +192,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fnv" @@ -253,15 +212,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - [[package]] name = "getrandom" version = "0.2.10" @@ -275,9 +225,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -306,21 +256,11 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", @@ -329,9 +269,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" @@ -347,39 +287,33 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -391,12 +325,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -405,22 +333,22 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" dependencies = [ "bit-set", - "bitflags 1.3.2", - "byteorder", + "bit-vec", + "bitflags 2.4.1", "lazy_static", "num-traits", "rand", @@ -432,17 +360,6 @@ dependencies = [ "unarray", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "qns_indexer" version = "0.1.0" @@ -451,13 +368,12 @@ dependencies = [ "alloy-sol-types", "anyhow", "bincode", - "cargo-component-bindings", "hex", "rmp-serde", "serde", "serde_json", - "thiserror", - "wit-bindgen 0.11.0", + "uqbar_process_lib", + "wit-bindgen", ] [[package]] @@ -468,9 +384,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -516,18 +432,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rmp" @@ -553,9 +469,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +checksum = "724fd11728a3804e9944b14cab63825024c40bf42f8af87c8b5d97c4bbacf426" dependencies = [ "proptest", "rand", @@ -582,11 +498,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -607,41 +523,41 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.167" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daf513456463b42aa1d94cff7e0c24d682b429f020b9afa4f5ba5c40a22b237" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.167" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69b106b68bc8054f0e974e70d19984040f8a5cf9215ca82626ea4853f82c4b9" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -650,9 +566,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smol_str" @@ -685,9 +601,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -703,14 +619,14 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", @@ -719,26 +635,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "thiserror" -version = "1.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -748,56 +644,17 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" @@ -812,14 +669,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand", + "serde", + "wit-bindgen", ] [[package]] @@ -828,12 +685,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "wait-timeout" version = "0.2.0" @@ -851,67 +702,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.30.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f8e9778e04cbf44f58acc301372577375a666b966c50b03ef46144f80436a8" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-encoder" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16" +checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.9.0" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51db59397fc650b5f2fc778e4a5c4456cd856bed7fc1ec15f8d3e28229dc463" +checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.30.0", - "wasmparser 0.108.0", -] - -[[package]] -name = "wasm-metadata" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be44e148f09a188971ec512250b3ae136029e2df586dd740586ce76a17ee657d" -dependencies = [ - "anyhow", - "indexmap", - "serde", - "serde_json", - "spdx", - "wasm-encoder 0.31.1", - "wasmparser 0.110.0", + "wasm-encoder", + "wasmparser", ] [[package]] name = "wasmparser" -version = "0.108.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c956109dcb41436a39391139d9b6e2d0a5e0b158e1293ef352ec977e5e36c5" -dependencies = [ - "indexmap", - "semver", -] - -[[package]] -name = "wasmparser" -version = "0.110.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dfcdb72d96f01e6c85b6bf20102e7423bdbaad5c337301bab2bbf253d26413c" +checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" dependencies = [ "indexmap", "semver", @@ -985,118 +803,83 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "wit-bindgen" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c3d15a04ce994fad2c5442a754b404ab1fee23c903a04a560f84f94fdf63c0" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "wit-bindgen-rust-macro", ] -[[package]] -name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" -dependencies = [ - "bitflags 2.3.3", -] - [[package]] name = "wit-bindgen-core" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9658ec54d4a3c9e2f079bc65a131093337595b595fbf82f805008469838cdea" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ "anyhow", - "wit-component 0.12.0", + "wit-component", "wit-parser", ] [[package]] name = "wit-bindgen-rust" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ae6a6198ba9765771b977e2af985a0d5ac71b59f999da5c4ee1c7bbd8ca8dc" -dependencies = [ - "heck", - "wasm-metadata 0.9.0", - "wit-bindgen-core", - "wit-bindgen-rust-lib", - "wit-component 0.12.0", -] - -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c31de8c6c77cac1fd4927c7584d1314cd5e838cfb40b53333d6dffc7a132dda" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ + "anyhow", "heck", + "wasm-metadata", "wit-bindgen-core", + "wit-component", ] [[package]] name = "wit-bindgen-rust-macro" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2abe5c7c4c08468d01590aa96c8a684dd94fb9241a248af88eef7edac61e43" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" dependencies = [ "anyhow", "proc-macro2", - "syn 2.0.28", + "quote", + "syn 2.0.39", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component 0.12.0", + "wit-component", ] [[package]] name = "wit-component" -version = "0.12.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "253bd426c532f1cae8c633c517c63719920535f3a7fada3589de40c5b734e393" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 1.3.2", + "bitflags 2.4.1", "indexmap", "log", - "wasm-encoder 0.30.0", - "wasm-metadata 0.9.0", - "wasmparser 0.108.0", - "wit-parser", -] - -[[package]] -name = "wit-component" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d843f4dfead0d465b09e8bfba4d3dcb1a1bcc857f87917d348c7fa401158bc5" -dependencies = [ - "anyhow", - "bitflags 2.3.3", - "indexmap", - "log", - "wasm-encoder 0.31.1", - "wasm-metadata 0.10.1", - "wasmparser 0.110.0", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.9.2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541efa2046e544de53a9da1e2f6299e63079840360c9e106f1f8275a97771318" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] [[package]] diff --git a/modules/qns_indexer/src/lib.rs b/modules/qns_indexer/src/lib.rs index 2ad5074d..21f11798 100644 --- a/modules/qns_indexer/src/lib.rs +++ b/modules/qns_indexer/src/lib.rs @@ -5,8 +5,10 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use std::collections::HashMap; use std::string::FromUtf8Error; -use uqbar_process_lib::uqbar::process::standard as wit; -use uqbar_process_lib::{Address, ProcessId, Request, Response}; +use uqbar_process_lib::{ + get_typed_state, receive, set_state, Address, Message, Payload, ProcessId, Request, + Response, +}; wit_bindgen::generate!({ path: "../../wit", @@ -101,14 +103,6 @@ fn subscribe_to_qns(from_block: u64) -> Vec { .to_vec() } -fn serialize_state(state: &State) -> anyhow::Result> { - Ok(bincode::serialize(state)?) -} - -fn deserialize_state(bytes: &[u8]) -> anyhow::Result { - Ok(bincode::deserialize(bytes)?) -} - fn serialize_message(message: &NetActions) -> anyhow::Result> { Ok(serde_json::to_vec(message)?) } @@ -117,7 +111,11 @@ fn deserialize_message(bytes: &[u8]) -> anyhow::Result { Ok(serde_json::from_slice(bytes)?) } -impl UqProcess for Component { +fn serialize_json_message(message: &serde_json::Value) -> anyhow::Result> { + Ok(serde_json::to_vec(message)?) +} + +impl Guest for Component { fn init(our: String) { let our = Address::from_str(&our).unwrap(); @@ -128,31 +126,28 @@ impl UqProcess for Component { }; // if we have state, load it in - match get_typed_state::(deserialize_state) { + match get_typed_state(|bytes| Ok(bincode::deserialize(bytes)?)) { Some(s) => { state = s; } None => {} } - print_to_terminal( - 0, - &format!("qns_indexer: starting at block {}", state.block), - ); + println!("qns_indexer: starting at block {}", state.block); match main(our, state) { Ok(_) => {} Err(e) => { - print_to_terminal(0, &format!("qns_indexer: ended with error: {:?}", e)); + println!("qns_indexer: ended with error: {:?}", e); } } } } -fn main(our: Address, state: State) -> anyhow::Result<()> { +fn main(our: Address, mut state: State) -> anyhow::Result<()> { // shove all state into net::net Request::new() - .target(Address::new(our.node, "net:sys:uqbar")?)? + .target(Address::new(&our.node, "net:sys:uqbar")?)? .ipc( &NetActions::QnsBatchUpdate(state.nodes.values().cloned().collect::>()), serialize_message, @@ -160,61 +155,58 @@ fn main(our: Address, state: State) -> anyhow::Result<()> { .send()?; Request::new() - .target(Address::new(our.node, "eth_rpc:sys:uqbar")?)? - .ipc_bytes(subscribe_to_qns(state.block - 1))? + .target(Address::new(&our.node, "eth_rpc:sys:uqbar")?)? + .ipc_bytes(subscribe_to_qns(state.block - 1)) .expects_response(5) .send()?; - let http_server_address = ProcessId::from_str("http_server:sys:uqbar").unwrap(); - Request::new() - .target(Address::new(our.node, http_server_address)?)? - .ipc_bytes( - json!({ + .target(Address::new(&our.node, "http_server:sys:uqbar")?)? + .ipc( + &json!({ "BindPath": { "path": "/node/:name", "authenticated": false, "local_only": false } - }) - .to_string() - .as_bytes(), + }), + serialize_json_message, )? .send()?; loop { let Ok((source, message)) = receive() else { - print_to_terminal(0, "qns_indexer: got network error"); + println!("qns_indexer: got network error"); continue; }; let Message::Request(request) = message else { // TODO we should store the subscription ID for eth_rpc // incase we want to cancel/reset it - // print_to_terminal(0, "qns_indexer: got response"); continue; }; - if source.process == http_server_address { + if source.process == "http_server:sys:uqbar" { if let Ok(ipc_json) = serde_json::from_slice::(&request.ipc) { if ipc_json["path"].as_str().unwrap_or_default() == "/node/:name" { if let Some(name) = ipc_json["url_params"]["name"].as_str() { if let Some(node) = state.nodes.get(name) { Response::new() - .ipc_bytes( - serde_json::json!({ + .ipc( + &serde_json::json!({ "status": 200, "headers": { "Content-Type": "application/json", }, - }) - .payload(&Payload { - mime: Some("application/json".to_string()), - bytes: serde_json::to_string(&node) - .unwrap() - .as_bytes() - .to_vec(), - })?, - ) + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("application/json".to_string()), + bytes: serde_json::to_string(&node) + .unwrap() + .as_bytes() + .to_vec(), + }) .send()?; continue; } @@ -222,24 +214,25 @@ fn main(our: Address, state: State) -> anyhow::Result<()> { } } Response::new() - .ipc_bytes( - serde_json::json!({ + .ipc( + &serde_json::json!({ "status": 404, "headers": { "Content-Type": "application/json", }, - }) - .payload(&Payload { - mime: Some("application/json".to_string()), - bytes: "Not Found".to_string().as_bytes().to_vec(), - })?, - ) + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("application/json".to_string()), + bytes: "Not Found".to_string().as_bytes().to_vec(), + }) .send()?; continue; } let Ok(msg) = serde_json::from_slice::(&request.ipc) else { - print_to_terminal(0, "qns_indexer: got invalid message"); + println!("qns_indexer: got invalid message"); continue; }; @@ -255,10 +248,10 @@ fn main(our: Address, state: State) -> anyhow::Result<()> { let decoded = NodeRegistered::decode_data(&decode_hex_to_vec(&e.data), true).unwrap(); let Ok(name) = dnswire_decode(decoded.0.clone()) else { - print_to_terminal( - 1, - &format!("qns_indexer: failed to decode name: {:?}", decoded.0), - ); + // print_to_terminal( + // 1, + // &format!("qns_indexer: failed to decode name: {:?}", decoded.0), + // ); continue; }; @@ -284,7 +277,10 @@ fn main(our: Address, state: State) -> anyhow::Result<()> { .collect::>(); let Some(name) = state.names.get(node) else { - print_to_terminal(0, &format!("qns_indexer: failed to find name for node during WsChanged: {:?}", node)); + println!( + "qns_indexer: failed to find name for node during WsChanged: {:?}", + node + ); continue; }; @@ -307,21 +303,17 @@ fn main(our: Address, state: State) -> anyhow::Result<()> { state.nodes.insert(name.clone(), update.clone()); Request::new() - .target(Address::new(our.node, "net:sys:uqbar")?)? + .target(Address::new(&our.node, "net:sys:uqbar")?)? .ipc(&NetActions::QnsUpdate(update.clone()), serialize_message)? .send()?; } event => { - print_to_terminal( - 0, - format!("qns_indexer: got unknown event: {:?}", event).as_str(), - ); + println!("qns_indexer: got unknown event: {:?}", event); } } } } - - set_typed_state::(&state, serialize_state); + set_state(&bincode::serialize(&state)?); } } // helpers diff --git a/modules/terminal/Cargo.toml b/modules/terminal/Cargo.toml index 63328db3..2796524d 100644 --- a/modules/terminal/Cargo.toml +++ b/modules/terminal/Cargo.toml @@ -23,6 +23,3 @@ crate-type = ["cdylib"] [package.metadata.component] package = "uqbar:process" - -[package.metadata.component.target] -path = "wit" diff --git a/process_lib/Cargo.toml b/process_lib/Cargo.toml index afbcf1b6..5627e3a1 100644 --- a/process_lib/Cargo.toml +++ b/process_lib/Cargo.toml @@ -4,9 +4,8 @@ version = "0.2.0" edition = "2021" [dependencies] -anyhow = "1.0.71" +anyhow = "1.0" bincode = "1.3.3" serde = {version = "1.0", features = ["derive"] } -serde_json = "1.0" rand = "0.8" wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } diff --git a/process_lib/src/kernel_types.rs b/process_lib/src/kernel_types.rs index d71da7c7..7e2323f9 100644 --- a/process_lib/src/kernel_types.rs +++ b/process_lib/src/kernel_types.rs @@ -363,31 +363,22 @@ impl std::fmt::Display for Message { match self { Message::Request(request) => write!( f, - "Request(\n inherit: {},\n expects_response: {:?},\n ipc: {},\n metadata: {}\n )", + "Request(\n inherit: {},\n expects_response: {:?},\n ipc: {} bytes,\n metadata: {}\n )", request.inherit, request.expects_response, - match serde_json::from_slice::(&request.ipc) { - Ok(json) => format!("{}", json), - Err(_) => format!("{:?}", request.ipc), - }, + request.ipc.len(), &request.metadata.as_ref().unwrap_or(&"None".into()), ), Message::Response((response, context)) => write!( f, - "Response(\n inherit: {},\n ipc: {},\n metadata: {},\n context: {}\n )", + "Response(\n inherit: {},\n ipc: {} bytes,\n metadata: {},\n context: {} bytes\n )", response.inherit, - match serde_json::from_slice::(&response.ipc) { - Ok(json) => format!("{}", json), - Err(_) => format!("{:?}", response.ipc), - }, + response.ipc.len(), &response.metadata.as_ref().unwrap_or(&"None".into()), if context.is_none() { - "None".into() + 0 } else { - match serde_json::from_slice::(&context.as_ref().unwrap()) { - Ok(json) => format!("{}", json), - Err(_) => format!("{:?}", context.as_ref().unwrap()), - } + context.as_ref().unwrap().len() }, ), } diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index b552727b..aefd67b9 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -355,8 +355,8 @@ impl Request { self } - pub fn payload(mut self, payload: Option) -> Self { - self.payload = payload; + pub fn payload(mut self, payload: Payload) -> Self { + self.payload = Some(payload); self } @@ -409,7 +409,7 @@ impl Request { &wit::Request { inherit: self.inherit, expects_response: self.timeout, - ipc: serde_json::to_vec(&ipc)?, + ipc, metadata: self.metadata, }, self.context.as_ref(), @@ -428,7 +428,7 @@ impl Request { &wit::Request { inherit: self.inherit, expects_response: self.timeout, - ipc: serde_json::to_vec(&ipc)?, + ipc, metadata: self.metadata, }, self.payload.as_ref(), @@ -479,8 +479,8 @@ impl Response { self } - pub fn payload(mut self, payload: Option) -> Self { - self.payload = payload; + pub fn payload(mut self, payload: Payload) -> Self { + self.payload = Some(payload); self } @@ -518,7 +518,7 @@ impl Response { crate::send_response( &wit::Response { inherit: self.inherit, - ipc: serde_json::to_vec(&ipc)?, + ipc, metadata: self.metadata, }, self.payload.as_ref(), @@ -566,14 +566,6 @@ where } } -pub fn set_typed_state(state: &T, serializer: F) -> anyhow::Result<()> -where - F: Fn(&T) -> anyhow::Result>, -{ - crate::set_state(&serializer(state)?); - Ok(()) -} - pub fn grant_messaging(our: &Address, grant_to: &Vec) -> anyhow::Result<()> { let Some(our_messaging_cap) = crate::get_capability(our, &"\"messaging\"".into()) else { // the kernel will always give us this capability, so this should never happen From b94bcaa6f9ac90e4008716a8c08a0a6c22080e44 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 14:31:04 -0500 Subject: [PATCH 30/40] types small fix --- src/types.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types.rs b/src/types.rs index 2eb89250..373bee23 100644 --- a/src/types.rs +++ b/src/types.rs @@ -409,8 +409,6 @@ pub type CapMessageReceiver = tokio::sync::mpsc::Receiver; // // types used for UQI: uqbar's identity system // -pub type PKINames = Arc>>; // TODO maybe U256 to String -pub type OnchainPKI = Arc>>; #[derive(Debug, Serialize, Deserialize)] pub struct Registration { From cc9067f2ce107a0894868bf3faf08af219a959d7 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 17:11:31 -0500 Subject: [PATCH 31/40] WIP: homepage, proxy, qns_indexer, terminal are working --- Cargo-component.lock | 3 - Cargo.lock | 399 ++++++--------- Cargo.toml | 4 +- build.rs | 2 +- modules/homepage/Cargo-component.lock | 3 - modules/homepage/Cargo.lock | 36 +- modules/homepage/Cargo.toml | 2 +- modules/http_proxy/Cargo-component.lock | 3 - modules/http_proxy/Cargo.lock | 280 ++++------ modules/http_proxy/Cargo.toml | 15 +- modules/http_proxy/src/lib.rs | 645 ++++++++++-------------- modules/http_proxy/src/process_lib.rs | 1 - modules/qns_indexer/Cargo.lock | 57 +-- modules/qns_indexer/Cargo.toml | 2 +- modules/terminal/Cargo.lock | 9 +- modules/terminal/Cargo.toml | 2 +- process_lib/Cargo.toml | 2 +- src/kernel/mod.rs | 4 +- wasi_snapshot_preview1.wasm | Bin 105098 -> 109411 bytes wit/uqbar.wit | 56 +- 20 files changed, 616 insertions(+), 909 deletions(-) delete mode 100644 Cargo-component.lock delete mode 100644 modules/homepage/Cargo-component.lock delete mode 100644 modules/http_proxy/Cargo-component.lock delete mode 120000 modules/http_proxy/src/process_lib.rs diff --git a/Cargo-component.lock b/Cargo-component.lock deleted file mode 100644 index 00bc239d..00000000 --- a/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/Cargo.lock b/Cargo.lock index 33f3c6cc..e1d6d8d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,22 +12,13 @@ dependencies = [ "regex", ] -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli 0.27.3", -] - [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.28.0", + "gimli", ] [[package]] @@ -246,12 +237,12 @@ version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ - "addr2line 0.21.0", + "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.32.1", + "object", "rustc-demangle", ] @@ -453,6 +444,18 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "cap-net-ext" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffc30dee200c20b4dcb80572226f42658e1d9c4b668656d7cc59c33d50e396e" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix 0.38.13", + "smallvec 1.11.0", +] + [[package]] name = "cap-primitives" version = "2.0.0" @@ -745,18 +748,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7348010242a23d0285e5f852f13b07f9540a50f13ab6e92fd047b88490bf5ee" +checksum = "2b5bb9245ec7dcc04d03110e538d31f0969d301c9d673145f4b4d5c3478539a3" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38849e3b19bc9a6dbf8bc188876b76e6ba288089a5567be573de50f44801375c" +checksum = "ebb18d10e5ddac43ba4ca8fd4e310938569c3e484cc01b6372b27dc5bb4dfd28" dependencies = [ "bumpalo", "cranelift-bforest", @@ -765,8 +768,8 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli 0.27.3", - "hashbrown 0.13.2", + "gimli", + "hashbrown 0.14.0", "log", "regalloc2", "smallvec 1.11.0", @@ -775,42 +778,43 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3de51da572e65cb712a47b7413c50208cac61a4201560038de929d9a7f4fadf" +checksum = "7a3ce6d22982c1b9b6b012654258bab1a13947bb12703518bef06b1a4867c3d6" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75f869ae826055a5064d4a400abde7806eb86d89765dbae51d42846df23121a" +checksum = "47220fd4f9a0ce23541652b6f16f83868d282602c600d14934b2a4c166b4bd80" [[package]] name = "cranelift-control" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf6631316ad6ccfd60055740ad25326330d31407a983a454e45c5a62f64d101" +checksum = "ed5a4c42672aea9b6e820046b52e47a1c05d3394a6cdf4cb3c3c4b702f954bd2" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1d6a38935ee64551a7c8da4cc759fdcaba1d951ec56336737c0459ed5a05d2" +checksum = "0b4e9a3296fc827f9d35135dc2c0c8dd8d8359eb1ef904bae2d55d5bcb0c9f94" dependencies = [ "serde", + "serde_derive", ] [[package]] name = "cranelift-frontend" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73c410c2d52e28fc4b49aab955a1c2f58580ff37a3b0641e23bccd6049e4b5" +checksum = "33ec537d0f0b8e084517f3e7bfa1d89af343d7c7df455573fca9f272d4e01267" dependencies = [ "cranelift-codegen", "log", @@ -820,15 +824,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61acaa7646020e0444bb3a22d212a5bae0e3b3969b18e1276a037ccd6493a8fd" +checksum = "45bab6d69919d210a50331d35cc6ce111567bc040aebac63a8ae130d0400a075" [[package]] name = "cranelift-native" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "543f52ef487498253ebe5df321373c5c314da74ada0e92f13451b6f887194f87" +checksum = "f32e81605f352cf37af5463f11cd7deec7b6572741931a8d372f7fdd4a744f5d" dependencies = [ "cranelift-codegen", "libc", @@ -837,9 +841,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.99.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788c27f41f31a50a9a3546b91253ad9495cd54df0d6533b3f3dcb4fb7a988f69" +checksum = "0edaa4cbec1bc787395c074233df2652dd62f3e29d3ee60329514a0a51e6b045" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -847,7 +851,7 @@ dependencies = [ "itertools 0.10.5", "log", "smallvec 1.11.0", - "wasmparser 0.110.0", + "wasmparser 0.115.0", "wasmtime-types", ] @@ -1334,19 +1338,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1704,9 +1695,9 @@ dependencies = [ [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" @@ -1741,16 +1732,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a481586acf778f1b1455424c343f71124b048ffa5f4fc3f8f6ae9dc432dcb3c7" -[[package]] -name = "file-per-thread-logger" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3cc21c33af89af0930c8cae4ade5e6fdc17b5d2c97b3d2e2edb67a1cf683f3" -dependencies = [ - "env_logger", - "log", -] - [[package]] name = "fixed-hash" version = "0.8.0" @@ -2009,22 +1990,16 @@ dependencies = [ "polyval 0.6.1", ] -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +dependencies = [ + "fallible-iterator", + "indexmap 2.0.0", + "stable_deref_trait", +] [[package]] name = "glob" @@ -2094,6 +2069,9 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", +] [[package]] name = "hashers" @@ -2220,12 +2198,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.27" @@ -2945,24 +2917,15 @@ dependencies = [ "syn 2.0.32", ] -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "crc32fast", - "hashbrown 0.13.2", - "indexmap 1.9.3", - "memchr", -] - [[package]] name = "object" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ + "crc32fast", + "hashbrown 0.14.0", + "indexmap 2.0.0", "memchr", ] @@ -3479,17 +3442,6 @@ dependencies = [ "trust-dns-proto", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "quote" version = "1.0.33" @@ -4578,15 +4530,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.48" @@ -5216,9 +5159,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-cap-std-sync" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cef338a20bd9e5e469a37b192b2a954c4dde83ea896c8eaf45df8c84cdf7be5" +checksum = "3fd94e147b273348ec68ae412b8bc17a4d372b9e070535b98e3e2c5a3ffd8e83" dependencies = [ "anyhow", "async-trait", @@ -5229,7 +5172,6 @@ dependencies = [ "fs-set-times", "io-extras", "io-lifetimes 2.0.2", - "is-terminal", "once_cell", "rustix 0.38.13", "system-interface", @@ -5240,9 +5182,9 @@ dependencies = [ [[package]] name = "wasi-common" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9c753bdf98fdc592fc729bda2248996f5dd1be71f4e01bf8c08225acb7b6bb" +checksum = "8d5166f7432ee36d06aa9f9bd7990a00330401fdbc75be7887ea952a299b9a19" dependencies = [ "anyhow", "bitflags 2.4.0", @@ -5326,18 +5268,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-encoder" -version = "0.31.1" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-encoder" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "9ca90ba1b5b0a70d3d49473c5579951f3bddc78d47b59256d2f9d4922b150aca" dependencies = [ "leb128", ] @@ -5369,19 +5302,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.110.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dfcdb72d96f01e6c85b6bf20102e7423bdbaad5c337301bab2bbf253d26413c" -dependencies = [ - "indexmap 2.0.0", - "semver", -] - -[[package]] -name = "wasmparser" -version = "0.112.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "e06c0641a4add879ba71ccb3a1e4278fd546f76f1eafb21d8f7b07733b547cd5" dependencies = [ "indexmap 2.0.0", "semver", @@ -5399,19 +5322,19 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.2.64" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ddf5892036cd4b780d505eff1194a0cbc10ed896097656fdcea3744b5e7c2f" +checksum = "8f98260aa20f939518bcec1fac32c78898d5c68872e7363a4651f21f791b6c7e" dependencies = [ "anyhow", - "wasmparser 0.112.0", + "wasmparser 0.116.0", ] [[package]] name = "wasmtime" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e38ee12eaafb34198cce001e2ea0a83d3884db5cf8e3af08864f108a2fb57c85" +checksum = "ca54f6090ce46973f33a79f265924b204f248f91aec09229bce53d19d567c1a6" dependencies = [ "anyhow", "async-trait", @@ -5423,16 +5346,17 @@ dependencies = [ "indexmap 2.0.0", "libc", "log", - "object 0.31.1", + "object", "once_cell", "paste", "psm", "rayon", "serde", + "serde_derive", "serde_json", "target-lexicon", - "wasm-encoder 0.31.1", - "wasmparser 0.110.0", + "wasm-encoder 0.35.0", + "wasmparser 0.115.0", "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", @@ -5448,27 +5372,27 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82313f9dce6f64dd08a7b51bef57411741b7eaef6b4611f77b91b6213a99808b" +checksum = "54984bc0b5689da87a43d7c181d23092b4d5cfcbb7ae3eb6b917dd55865d95e6" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d22677d863d88d0ee05a07bfe28fdc5525149b6ea5a108f1fa2796fa86d75b8" +checksum = "1a4df7655bb73b592189033ab046aa47c1da486d70bc9c1ebf45e55ac030bdf4" dependencies = [ "anyhow", "base64 0.21.4", "bincode", "directories-next", - "file-per-thread-logger", "log", "rustix 0.38.13", "serde", + "serde_derive", "sha2 0.10.7", "toml 0.5.11", "windows-sys", @@ -5477,9 +5401,9 @@ dependencies = [ [[package]] name = "wasmtime-component-macro" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b6da03d55c656066ebc93d27ce54de11fcd2d3157e7490c6196a65aa1e9bc0" +checksum = "64de99fb7c4c383832b85efcaae95f7094a5c505d80146227ce97ab436cbac68" dependencies = [ "anyhow", "proc-macro2", @@ -5487,34 +5411,35 @@ dependencies = [ "syn 2.0.32", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.9.2", + "wit-parser", ] [[package]] name = "wasmtime-component-util" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b54327f9ce6a46c6841c43d93c4fa366cd0beb0f075743b120d31a3d6afe34fd" +checksum = "9f9141a8df069e106eee0c3a8173c0809cf1a4b5630628cfb1f25ab114720093" [[package]] name = "wasmtime-cranelift" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d52e14e5453e82708816e992140c59e511bbf7c0868ee654100e2792483f56" +checksum = "1cf3cee8be02f5006d21b773ffd6802f96a0b7d661ff2ad8a01fb93df458b1aa" dependencies = [ "anyhow", + "cfg-if", "cranelift-codegen", "cranelift-control", "cranelift-entity", "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.27.3", + "gimli", "log", - "object 0.31.1", + "object", "target-lexicon", "thiserror", - "wasmparser 0.110.0", + "wasmparser 0.115.0", "wasmtime-cranelift-shared", "wasmtime-environ", "wasmtime-versioned-export-macros", @@ -5522,37 +5447,38 @@ dependencies = [ [[package]] name = "wasmtime-cranelift-shared" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ddb7f34fff5b4a01aa2e55373fceb1b59d5f981abca44afdd63d7dd39689047" +checksum = "420fd2a69bc162957f4c94f21c7fa08ecf60d916f4e87b56332507c555da381d" dependencies = [ "anyhow", "cranelift-codegen", "cranelift-control", "cranelift-native", - "gimli 0.27.3", - "object 0.31.1", + "gimli", + "object", "target-lexicon", "wasmtime-environ", ] [[package]] name = "wasmtime-environ" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad336809866b743410ac86ec0bdc34899d6f1af5d3deed97188e90503ff527d7" +checksum = "fb6a445ce2b2810127caee6c1b79b8da4ae57712b05556a674592c18b7500a14" dependencies = [ "anyhow", "cranelift-entity", - "gimli 0.27.3", + "gimli", "indexmap 2.0.0", "log", - "object 0.31.1", + "object", "serde", + "serde_derive", "target-lexicon", "thiserror", - "wasm-encoder 0.31.1", - "wasmparser 0.110.0", + "wasm-encoder 0.35.0", + "wasmparser 0.115.0", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -5560,9 +5486,9 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc69f0a316db37482ebc83669236ea7c943d0b49a1a23f763061c9fc9d07d0b" +checksum = "345a8b061c9eab459e10b9112df9fc357d5a9e8b5b1004bc5fc674fba9be6d2a" dependencies = [ "cc", "cfg-if", @@ -5574,22 +5500,23 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2004b30ea1ad9fd288bce54af19ef08281250e1087f0b5ffc6ca06bacd821edb" +checksum = "1f0f6586c61125fbfc13c3108c3dd565d21f314dd5bac823b9a5b7ab576d21f1" dependencies = [ - "addr2line 0.20.0", + "addr2line", "anyhow", "bincode", "cfg-if", "cpp_demangle", - "gimli 0.27.3", + "gimli", "ittapi", "log", - "object 0.31.1", + "object", "rustc-demangle", "rustix 0.38.13", "serde", + "serde_derive", "target-lexicon", "wasmtime-environ", "wasmtime-jit-debug", @@ -5600,11 +5527,11 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54aa8081162b13a96f47ab40f9aa03fc02dad38ee10b1418243ac8517c5af6d3" +checksum = "109a9e46afe33580b952b14a4207354355f19bcdf0b47485b397b68409eaf553" dependencies = [ - "object 0.31.1", + "object", "once_cell", "rustix 0.38.13", "wasmtime-versioned-export-macros", @@ -5612,9 +5539,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2922462d01f5c112bbc4e6eb95ee68447a6031c0b90cc2ad69b890060b3842d9" +checksum = "f67e6be36375c39cff57ed3b137ab691afbf2d9ba8ee1c01f77888413f218749" dependencies = [ "cfg-if", "libc", @@ -5623,9 +5550,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "536c34c4abbe22c40f631067b57ca14d719faf3f63ae0d504014a4d15a4b980b" +checksum = "1d07986b2327b5e7f535ed638fbde25990fc8f85400194fda0d26db71c7b685e" dependencies = [ "anyhow", "cc", @@ -5641,32 +5568,34 @@ dependencies = [ "rand", "rustix 0.38.13", "sptr", - "wasm-encoder 0.31.1", + "wasm-encoder 0.35.0", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-fiber", "wasmtime-jit-debug", "wasmtime-versioned-export-macros", + "wasmtime-wmemcheck", "windows-sys", ] [[package]] name = "wasmtime-types" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec6f1e74eb5ef817043b243eae37cc0e424c256c4069ab2c5afd9f3fe91a12ee" +checksum = "e810a0d2e869abd1cb42bd232990f6bd211672b3d202d2ae7e70ffb97ed70ea3" dependencies = [ "cranelift-entity", "serde", + "serde_derive", "thiserror", - "wasmparser 0.110.0", + "wasmparser 0.115.0", ] [[package]] name = "wasmtime-versioned-export-macros" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ca36fa6cad8ef885bc27d7d50c8b1cb7da0534251188a824f4953b07875703" +checksum = "09b5575a75e711ca6c36bb9ad647c93541cdc8e34218031acba5da3f35919dd3" dependencies = [ "proc-macro2", "quote", @@ -5675,28 +5604,32 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269f4f2192b18037729b617eadb512e95510f1b0cd8fb4990aef286c9bb3dfb9" +checksum = "1e6730a2853226292cee755a36549dd1a443b324cf99319cb390af1afed6cb8a" dependencies = [ "anyhow", "async-trait", "bitflags 2.4.0", "bytes", "cap-fs-ext", + "cap-net-ext", "cap-rand", "cap-std", "cap-time-ext", "fs-set-times", "futures", "io-extras", + "io-lifetimes 2.0.2", "libc", + "log", "once_cell", "rustix 0.38.13", "system-interface", "thiserror", "tokio", "tracing", + "url", "wasi-cap-std-sync", "wasi-common", "wasmtime", @@ -5706,16 +5639,16 @@ dependencies = [ [[package]] name = "wasmtime-winch" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d016c3f1d0c8ac905bfda51936cb6dae040e0d8edc75b7a1ef9f21773a19f6" +checksum = "c1c1b6abbba5a01739bef9f00a87b419414a7dd99b795823d93fb12fc2bf994a" dependencies = [ "anyhow", "cranelift-codegen", - "gimli 0.27.3", - "object 0.31.1", + "gimli", + "object", "target-lexicon", - "wasmparser 0.110.0", + "wasmparser 0.115.0", "wasmtime-cranelift-shared", "wasmtime-environ", "winch-codegen", @@ -5723,16 +5656,22 @@ dependencies = [ [[package]] name = "wasmtime-wit-bindgen" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd55caadebae32cf18541e5077b3f042a171bb9988ea0040d0569f26a63227d" +checksum = "9d214ca7513d76af2872ad5bba4b0dcc0225821931745fdcb4fc30dd34bc3bf7" dependencies = [ "anyhow", "heck", "indexmap 2.0.0", - "wit-parser 0.9.2", + "wit-parser", ] +[[package]] +name = "wasmtime-wmemcheck" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dafab2db172a53e23940e0fa3078c202f567ee5f13f4b42f66b694fab43c658" + [[package]] name = "wast" version = "35.0.2" @@ -5744,23 +5683,23 @@ dependencies = [ [[package]] name = "wast" -version = "64.0.0" +version = "67.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" +checksum = "36c2933efd77ff2398b83817a98984ffe4b67aefd9aa1d2c8e68e19b553f1c38" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.32.0", + "wasm-encoder 0.36.1", ] [[package]] name = "wat" -version = "1.0.71" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" +checksum = "c02905d13751dcb18f4e19f489d37a1bf139f519feaeef28d072a41a78e69a74" dependencies = [ - "wast 64.0.0", + "wast 67.0.0", ] [[package]] @@ -5790,9 +5729,9 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "wiggle" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166189cd49163adc9a1e2a33b33625eb934d06e518c318905c3a5140d9bc1d45" +checksum = "7f6ce56a4019ce3d8592c298029a75abe6887d1c95a078a4c53ec77a0628262d" dependencies = [ "anyhow", "async-trait", @@ -5805,9 +5744,9 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67571bd77bff962190744adb29e72a1157d30e8d34fbb2c1c7b0ad7627be020" +checksum = "e585a4b1e84195031c77d8484af99cd93f129f45d519e83cb8cc75e9a420cfd3" dependencies = [ "anyhow", "heck", @@ -5820,9 +5759,9 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "12.0.1" +version = "14.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5677f7d740bc41f9f6af4a6a719a07fbe1aa8ec66e0ec1ca4d3617f2b27d5361" +checksum = "c6f321dbce722989d65c3082dba479fa392c7b7a1a4c3adc2a39545dd5aa452f" dependencies = [ "proc-macro2", "quote", @@ -5863,17 +5802,17 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "0.10.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e6f2f344ec89998f047d0aa3aec77088eb8e33c91f5efdd191b140fda6fa40" +checksum = "f112bebb367a544d20c254083798087f22ceeb426168a970b955e8436f749dca" dependencies = [ "anyhow", "cranelift-codegen", - "gimli 0.27.3", + "gimli", "regalloc2", "smallvec 1.11.0", "target-lexicon", - "wasmparser 0.110.0", + "wasmparser 0.115.0", "wasmtime-environ", ] @@ -5984,7 +5923,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags 2.4.0", "wit-bindgen-rust-macro", @@ -5993,17 +5932,17 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", - "wit-parser 0.12.2", + "wit-parser", ] [[package]] name = "wit-bindgen-rust" version = "0.13.2" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -6015,7 +5954,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", @@ -6042,23 +5981,7 @@ dependencies = [ "wasm-encoder 0.36.1", "wasm-metadata", "wasmparser 0.116.0", - "wit-parser 0.12.2", -] - -[[package]] -name = "wit-parser" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541efa2046e544de53a9da1e2f6299e63079840360c9e106f1f8275a97771318" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.0.0", - "log", - "pulldown-cmark", - "semver", - "unicode-xid", - "url", + "wit-parser", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5df58169..a874d904 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,6 @@ url = "*" uqbar_process_lib = { path = "process_lib" } uuid = { version = "1.1.2", features = ["serde", "v4"] } warp = "0.3.5" -wasmtime = "12.0.1" -wasmtime-wasi = "12.0.1" +wasmtime = "14.0.4" +wasmtime-wasi = "14.0.4" zip = "0.6" diff --git a/build.rs b/build.rs index 1366c0fc..6e341bde 100644 --- a/build.rs +++ b/build.rs @@ -153,7 +153,7 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if package_name != "homepage" { + if !["homepage", "http_proxy", "qns_indexer", "terminal"].contains(&package_name) { continue; } diff --git a/modules/homepage/Cargo-component.lock b/modules/homepage/Cargo-component.lock deleted file mode 100644 index 00bc239d..00000000 --- a/modules/homepage/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/modules/homepage/Cargo.lock b/modules/homepage/Cargo.lock index ab1f6a96..8204a107 100644 --- a/modules/homepage/Cargo.lock +++ b/modules/homepage/Cargo.lock @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "cfg-if" @@ -122,9 +122,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -182,18 +182,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd" dependencies = [ "proc-macro2", "quote", @@ -202,9 +202,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -228,9 +228,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -239,9 +239,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" @@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -319,7 +319,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.13.2" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -341,7 +341,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", diff --git a/modules/homepage/Cargo.toml b/modules/homepage/Cargo.toml index c04d5a0c..5393be2c 100644 --- a/modules/homepage/Cargo.toml +++ b/modules/homepage/Cargo.toml @@ -15,7 +15,7 @@ anyhow = "1.0" bincode = "1.3.3" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } uqbar_process_lib = { path = "../../process_lib" } [lib] diff --git a/modules/http_proxy/Cargo-component.lock b/modules/http_proxy/Cargo-component.lock deleted file mode 100644 index 00bc239d..00000000 --- a/modules/http_proxy/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/modules/http_proxy/Cargo.lock b/modules/http_proxy/Cargo.lock index a8af8774..eb0a1c80 100644 --- a/modules/http_proxy/Cargo.lock +++ b/modules/http_proxy/Cargo.lock @@ -19,39 +19,15 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] -name = "bitflags" -version = "2.4.0" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#aa6e3c1168273b5cf6221fa0206f07f2ffb8567d" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#aa6e3c1168273b5cf6221fa0206f07f2ffb8567d" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "equivalent" @@ -60,19 +36,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "form_urlencoded" -version = "1.2.0" +name = "getrandom" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "percent-encoding", + "cfg-if", + "libc", + "wasi", ] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -85,13 +63,13 @@ dependencies = [ [[package]] name = "http_proxy" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "serde", "serde_json", + "uqbar_process_lib", "wit-bindgen", ] @@ -101,21 +79,11 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", @@ -134,6 +102,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + [[package]] name = "log" version = "0.4.20" @@ -141,37 +115,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] -name = "memchr" -version = "2.6.3" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "quote" version = "1.0.33" @@ -181,6 +138,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "ryu" version = "1.0.15" @@ -189,24 +176,24 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd" dependencies = [ "proc-macro2", "quote", @@ -215,9 +202,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -226,9 +213,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "spdx" @@ -241,59 +228,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.31" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" @@ -308,40 +256,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand", + "serde", + "wit-bindgen", ] [[package]] -name = "version_check" -version = "0.9.4" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -350,9 +299,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -360,19 +309,17 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ - "bitflags 2.4.0", + "bitflags", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -381,54 +328,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -438,16 +373,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/http_proxy/Cargo.toml b/modules/http_proxy/Cargo.toml index 1d78d2ce..08aa21dc 100644 --- a/modules/http_proxy/Cargo.toml +++ b/modules/http_proxy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_proxy" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,20 +11,15 @@ opt-level = "s" lto = true [dependencies] -anyhow = "1.0.72" +anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } +uqbar_process_lib = { path = "../../process_lib" } [lib] crate-type = ["cdylib"] [package.metadata.component] -package = "component:uq-process" - -[package.metadata.component.target] -path = "wit" - -[package.metadata.component.dependencies] +package = "uqbar:process" diff --git a/modules/http_proxy/src/lib.rs b/modules/http_proxy/src/lib.rs index 289bbad4..bba2e600 100644 --- a/modules/http_proxy/src/lib.rs +++ b/modules/http_proxy/src/lib.rs @@ -1,42 +1,60 @@ -cargo_component_bindings::generate!(); - -use serde::{Deserialize, Serialize}; use serde_json::json; use std::collections::HashMap; - -use bindings::component::uq_process::types::*; -use bindings::{ - get_payload, print_to_terminal, receive, send_request, send_requests, send_response, Guest, +use uqbar_process_lib::{ + get_payload, receive, println, Address, Message, Payload, Request, Response, }; -#[allow(dead_code)] -mod process_lib; +wit_bindgen::generate!({ + path: "../../wit", + world: "process", + exports: { + world: Component, + }, +}); + +struct Component; +impl Guest for Component { + fn init(our: String) { + let our = Address::from_str(&our).unwrap(); + //print_to_terminal(1, "http_proxy: start"); + + match main(our) { + Ok(_) => {} + Err(e) => { + println!("http_proxy: ended with error: {:?}", e); + } + } + } +} const PROXY_HOME_PAGE: &str = include_str!("http_proxy.html"); -struct Component; - -fn send_http_response(status: u16, headers: HashMap, payload_bytes: Vec) { - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "status": status, - "headers": headers, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("text/html".to_string()), - bytes: payload_bytes, - }), - ) +fn serialize_json_message(message: &serde_json::Value) -> anyhow::Result> { + Ok(serde_json::to_vec(message)?) } -fn send_not_found() { +fn send_http_response( + status: u16, + headers: HashMap, + payload_bytes: Vec, +) -> anyhow::Result<()> { + Response::new() + .ipc( + &json!({ + "status": status, + "headers": headers, + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("text/html".to_string()), + bytes: payload_bytes, + }) + .send()?; + Ok(()) +} + +fn send_not_found() -> anyhow::Result<()> { send_http_response( 404, HashMap::new(), @@ -44,359 +62,232 @@ fn send_not_found() { ) } -impl Guest for Component { - fn init(our: Address) { - print_to_terminal(1, "http_proxy: start"); +fn main(our: Address) -> anyhow::Result<()> { + let mut registrations: HashMap = HashMap::new(); - let mut registrations: HashMap = HashMap::new(); + // bind to all of our favorite paths + for path in ["/", "/static/*", "/list", "/register", "/serve/:username/*"] { + Request::new() + .ipc( + &json!({ + "BindPath": { + "path": path, + "authenticated": true, + "local_only": false + } + }), + serialize_json_message, + )? + .send()?; + } - let bindings_address = Address { - node: our.node.clone(), - process: ProcessId::from_str("http_server:sys:uqbar").unwrap(), + loop { + let Ok((_source, message)) = receive() else { + //print_to_terminal(0, "http_proxy: got network error"); + let mut headers = HashMap::new(); + headers.insert("Content-Type".to_string(), "text/html".to_string()); + send_http_response( + 503, + headers, + format!("

Node Offline

").as_bytes().to_vec(), + )?; + continue; + }; + let Message::Request(request) = message else { + println!("http_proxy: got unexpected message"); + continue; }; - // , option> - let http_endpoint_binding_requests: [(Address, Request, Option, Option); - 5] = [ - ( - bindings_address.clone(), - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": "/", - "authenticated": true, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ), - ( - bindings_address.clone(), - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": "/static/*", - "authenticated": true, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ), - ( - bindings_address.clone(), - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": "/list", - "authenticated": true, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ), - ( - bindings_address.clone(), - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": "/register", - "authenticated": true, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ), - ( - bindings_address.clone(), - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": "/serve/:username/*", - "authenticated": true, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ), - ]; - send_requests(&http_endpoint_binding_requests); - - loop { - let Ok((_source, message)) = receive() else { - print_to_terminal(0, "http_proxy: got network error"); - let mut headers = HashMap::new(); - headers.insert("Content-Type".to_string(), "text/html".to_string()); - send_http_response(503, headers, format!("

Node Offline

").as_bytes().to_vec()); + let message_json: serde_json::Value = match serde_json::from_slice(&request.ipc) { + Ok(v) => v, + Err(_) => { + //print_to_terminal(1, "http_proxy: failed to parse ipc JSON, skipping"); continue; - }; - let Message::Request(request) = message else { - print_to_terminal(0, "http_proxy: got unexpected message"); - continue; - }; - - let message_json: serde_json::Value = match serde_json::from_slice(&request.ipc) { - Ok(v) => v, - Err(_) => { - print_to_terminal(1, "http_proxy: failed to parse ipc JSON, skipping"); - continue; - } - }; - - print_to_terminal( - 1, - format!("http_proxy: got request: {}", message_json).as_str(), - ); - - if message_json["path"] == "/" && message_json["method"] == "GET" { - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "action": "response", - "status": 200, - "headers": { - "Content-Type": "text/html", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("text/html".to_string()), - bytes: PROXY_HOME_PAGE - .replace("${our}", &our.node) - .as_bytes() - .to_vec(), - }), - ); - } else if message_json["path"] == "/list" && message_json["method"] == "GET" { - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "action": "response", - "status": 200, - "headers": { - "Content-Type": "application/json", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: serde_json::json!({"registrations": registrations}) - .to_string() - .as_bytes() - .to_vec(), - }), - ); - } else if message_json["path"] == "/register" && message_json["method"] == "POST" { - let mut status = 204; - - let Some(payload) = get_payload() else { - print_to_terminal(1, "/register POST with no bytes"); - continue; - }; - - let body: serde_json::Value = match serde_json::from_slice(&payload.bytes) { - Ok(s) => s, - Err(e) => { - print_to_terminal(1, format!("Bad body format: {}", e).as_str()); - continue; - } - }; - - let username = body["username"].as_str().unwrap_or(""); - - print_to_terminal(1, format!("Register proxy for: {}", username).as_str()); - - if !username.is_empty() { - registrations.insert(username.to_string(), "foo".to_string()); - } else { - status = 400; - } - - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "action": "response", - "status": status, - "headers": { - "Content-Type": "text/html", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("text/html".to_string()), - bytes: (if status == 400 { - "Bad Request" - } else { - "Success" - }) - .to_string() - .as_bytes() - .to_vec(), - }), - ); - } else if message_json["path"] == "/register" && message_json["method"] == "DELETE" { - print_to_terminal(1, "HERE IN /register to delete something"); - let username = message_json["query_params"]["username"] - .as_str() - .unwrap_or(""); - - let mut status = 204; - - if !username.is_empty() { - registrations.remove(username); - } else { - status = 400; - } - - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ - "action": "response", - "status": status, - "headers": { - "Content-Type": "text/html", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("text/html".to_string()), - bytes: (if status == 400 { - "Bad Request" - } else { - "Success" - }) - .to_string() - .as_bytes() - .to_vec(), - }), - ); - } else if message_json["path"] == "/serve/:username/*" { - let username = message_json["url_params"]["username"] - .as_str() - .unwrap_or(""); - let raw_path = message_json["raw_path"].as_str().unwrap_or(""); - print_to_terminal(1, format!("proxy for user: {}", username).as_str()); - - if username.is_empty() || raw_path.is_empty() { - send_not_found(); - } else if !registrations.contains_key(username) { - send_response( - &Response { - inherit: false, - ipc: json!({ - "action": "response", - "status": 403, - "headers": { - "Content-Type": "text/html", - }, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("text/html".to_string()), - bytes: "Not Authorized".to_string().as_bytes().to_vec(), - }), - ); - } else { - let path_parts: Vec<&str> = raw_path.split('/').collect(); - let mut proxied_path = "/".to_string(); - - if let Some(pos) = path_parts.iter().position(|&x| x == "serve") { - proxied_path = format!("/{}", path_parts[pos + 2..].join("/")); - print_to_terminal(1, format!("Path to proxy: {}", proxied_path).as_str()); - } - - let payload = get_payload(); - - send_request( - &Address { - node: username.into(), - process: ProcessId::from_str("http_server:sys:uqbar").unwrap(), - }, - &Request { - inherit: true, - expects_response: None, - ipc: json!({ - "method": message_json["method"], - "path": proxied_path, - "headers": message_json["headers"], - "proxy_path": raw_path, - "query_params": message_json["query_params"], - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - payload.as_ref(), - ); - } - } else { - send_not_found(); } + }; + + //print_to_terminal( + // 1, + // format!("http_proxy: got request: {}", message_json).as_str(), + //); + + if message_json["path"] == "/" && message_json["method"] == "GET" { + Response::new() + .ipc( + &json!({ + "action": "response", + "status": 200, + "headers": { + "Content-Type": "text/html", + }, + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("text/html".to_string()), + bytes: PROXY_HOME_PAGE + .replace("${our}", &our.node) + .as_bytes() + .to_vec(), + }) + .send()?; + } else if message_json["path"] == "/list" && message_json["method"] == "GET" { + Response::new() + .ipc( + &json!({ + "action": "response", + "status": 200, + "headers": { + "Content-Type": "application/json", + }, + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("application/json".to_string()), + bytes: serde_json::json!({"registrations": registrations}) + .to_string() + .as_bytes() + .to_vec(), + }) + .send()?; + } else if message_json["path"] == "/register" && message_json["method"] == "POST" { + let mut status = 204; + + let Some(payload) = get_payload() else { + //print_to_terminal(1, "/register POST with no bytes"); + continue; + }; + + let body: serde_json::Value = match serde_json::from_slice(&payload.bytes) { + Ok(s) => s, + Err(e) => { + //print_to_terminal(1, format!("Bad body format: {}", e).as_str()); + continue; + } + }; + + let username = body["username"].as_str().unwrap_or(""); + + //print_to_terminal(1, format!("Register proxy for: {}", username).as_str()); + + if !username.is_empty() { + registrations.insert(username.to_string(), "foo".to_string()); + } else { + status = 400; + } + + Response::new() + .ipc( + &json!({ + "action": "response", + "status": 200, + "headers": { + "Content-Type": "text/html", + }, + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("text/html".to_string()), + bytes: (if status == 400 { + "Bad Request" + } else { + "Success" + }) + .to_string() + .as_bytes() + .to_vec(), + }) + .send()?; + } else if message_json["path"] == "/register" && message_json["method"] == "DELETE" { + //print_to_terminal(1, "HERE IN /register to delete something"); + let username = message_json["query_params"]["username"] + .as_str() + .unwrap_or(""); + + let mut status = 204; + + if !username.is_empty() { + registrations.remove(username); + } else { + status = 400; + } + + Response::new() + .ipc( + &json!({ + "action": "response", + "status": status, + "headers": { + "Content-Type": "text/html", + }, + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("text/html".to_string()), + bytes: (if status == 400 { + "Bad Request" + } else { + "Success" + }) + .to_string() + .as_bytes() + .to_vec(), + }) + .send()?; + } else if message_json["path"] == "/serve/:username/*" { + let username = message_json["url_params"]["username"] + .as_str() + .unwrap_or(""); + let raw_path = message_json["raw_path"].as_str().unwrap_or(""); + //print_to_terminal(1, format!("proxy for user: {}", username).as_str()); + + if username.is_empty() || raw_path.is_empty() { + send_not_found()?; + } else if !registrations.contains_key(username) { + Response::new() + .ipc( + &json!({ + "action": "response", + "status": 403, + "headers": { + "Content-Type": "text/html", + }, + }), + serialize_json_message, + )? + .payload(Payload { + mime: Some("text/html".to_string()), + bytes: "Not Authorized".to_string().as_bytes().to_vec(), + }) + .send()?; + } else { + let path_parts: Vec<&str> = raw_path.split('/').collect(); + let mut proxied_path = "/".to_string(); + + if let Some(pos) = path_parts.iter().position(|&x| x == "serve") { + proxied_path = format!("/{}", path_parts[pos + 2..].join("/")); + //print_to_terminal(1, format!("Path to proxy: {}", proxied_path).as_str()); + } + + Request::new() + .inherit(true) + .ipc( + &json!({ + "method": message_json["method"], + "path": proxied_path, + "headers": message_json["headers"], + "proxy_path": raw_path, + "query_params": message_json["query_params"], + }), + serialize_json_message, + )? + .send()?; + } + } else { + send_not_found()?; } } } diff --git a/modules/http_proxy/src/process_lib.rs b/modules/http_proxy/src/process_lib.rs deleted file mode 120000 index 77367fe0..00000000 --- a/modules/http_proxy/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../src/process_lib.rs \ No newline at end of file diff --git a/modules/qns_indexer/Cargo.lock b/modules/qns_indexer/Cargo.lock index fc26bb2c..61c5340a 100644 --- a/modules/qns_indexer/Cargo.lock +++ b/modules/qns_indexer/Cargo.lock @@ -113,12 +113,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.5.0" @@ -369,7 +363,6 @@ dependencies = [ "anyhow", "bincode", "hex", - "rmp-serde", "serde", "serde_json", "uqbar_process_lib", @@ -445,28 +438,6 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" -[[package]] -name = "rmp" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" -dependencies = [ - "byteorder", - "rmp", - "serde", -] - [[package]] name = "ruint" version = "1.11.0" @@ -535,18 +506,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.190" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd" dependencies = [ "proc-macro2", "quote", @@ -702,18 +673,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.10" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", @@ -727,9 +698,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.116.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -804,7 +775,7 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "wit-bindgen" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags 2.4.1", "wit-bindgen-rust-macro", @@ -813,7 +784,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -823,7 +794,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.13.2" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -835,7 +806,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#23e0c1463f31cb53fb390a7c744a3ced282a579a" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", diff --git a/modules/qns_indexer/Cargo.toml b/modules/qns_indexer/Cargo.toml index 66d522a8..aa3d610c 100644 --- a/modules/qns_indexer/Cargo.toml +++ b/modules/qns_indexer/Cargo.toml @@ -18,7 +18,7 @@ bincode = "1.3.3" hex = "0.4.3" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } uqbar_process_lib = { path = "../../process_lib" } [lib] diff --git a/modules/terminal/Cargo.lock b/modules/terminal/Cargo.lock index 53004782..953667c4 100644 --- a/modules/terminal/Cargo.lock +++ b/modules/terminal/Cargo.lock @@ -263,7 +263,6 @@ dependencies = [ "bincode", "rand", "serde", - "serde_json", "wit-bindgen", ] @@ -311,7 +310,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -320,7 +319,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -330,7 +329,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.13.2" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -342,7 +341,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", diff --git a/modules/terminal/Cargo.toml b/modules/terminal/Cargo.toml index 2796524d..1d268d17 100644 --- a/modules/terminal/Cargo.toml +++ b/modules/terminal/Cargo.toml @@ -15,7 +15,7 @@ anyhow = "1.0" bincode = "1.3.3" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } uqbar_process_lib = { path = "../../process_lib" } [lib] diff --git a/process_lib/Cargo.toml b/process_lib/Cargo.toml index 5627e3a1..bd6e3cbb 100644 --- a/process_lib/Cargo.toml +++ b/process_lib/Cargo.toml @@ -8,4 +8,4 @@ anyhow = "1.0" bincode = "1.3.3" serde = {version = "1.0", features = ["derive"] } rand = "0.8" -wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index f4f5fc9e..68fc3ce1 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1113,8 +1113,8 @@ async fn make_process_loop( let mut linker = Linker::new(&engine); Process::add_to_linker(&mut linker, |state: &mut ProcessWasi| state).unwrap(); - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table).unwrap(); + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); // (&mut table).unwrap(); wasmtime_wasi::preview2::command::add_to_linker(&mut linker).unwrap(); // wasmtime_wasi::preview2::bindings::clocks::wall_clock::add_to_linker(&mut linker, |t| t) diff --git a/wasi_snapshot_preview1.wasm b/wasi_snapshot_preview1.wasm index 87e85057107bc0c043e569ea72707f48a77e8e37..9b621b988c95abc793f1e0c49e2290495b04dcc4 100644 GIT binary patch literal 109411 zcmeFa31DPbc_w;K-P)wmQoY&j?zXuqx9#>KbyZ2Kl5CbPL17z6Y?cHkgvhnrYL!}2 zsjEueZm``33^)N2_Lx92CJM zkA41r{2M!eUj9b$i`xW$@IUMBxOWk`|GYSlD)5gj-{66DxBMQDk}S^u^AT!SHS#tk zEAU?8JM};IMAd{JbVT0BCS-Yad%t|ge^vi*fw%HwK9Co(t7sALyk6sSNWSJUaihl| zOQUlf^LhMBFyJoos}m<|ckN7k)m?41&qXZjrn8k!^X5i##qFHybluhC>utBS?yhxC zWT)~|*~xZoGM~*)Pv)|dxw-L^Zg;ZYY`e8?tKD=vmUV+HUR!C^PIZo-t*op}%IkiG zhudzawX)G|w$>~Q-=bMvZnd@6>bBOJwVf3f7tyG_);fFft;tg2`d-j<&#k+C;c~^x zR^9GWtN!x3+o`pi>wwNwrGBQ_ajnSCYq{oSgwRI2=1$hzt@W3CfIc_ru65hzCOh4> zTUoWNo!5AzPro|n)@qaWO1EO!JFn<)pNfbJgvb-Er-OPr%~!Zqbm#p!*rz{d+s&?P z#dcoFfdK0#-FCaxo~*U%7|zvl-kYuC3gCTbe1Fee$?_L7H4}YCfa$IK=X^2H>etWhX;9(o;igqia^Nb9i3fXGp}hi$RcU`_xebT)LnM&3DdCR=QSv=iR!Z zXM6#TUd(*;>sT91^vvV$SU>7)yK9wImm^HPmC%6l+TEyrzCwxv?fI zBC$xwUo7h$>TFcW6|1f_v=e-Mnm3vdB2ERh4DY0ttGrrFf-oy_JStyrt*rEuSC{wT z4Zo{YS6s`wV9_CdJGs*Abgk62vYmEit=?Kau73KpmL@w@uP3YLx{$3S=1|H`}eXRfvgxRZFRDd}CQVE;`CyRoW*v zSU1G|&M_(D!9O6kDaqf?G%Fr^x(J4Ub)V`a!=HS7^&Ra~Uu7M7Pu;BWA|zx#VkYn! zE0vR-i{XI%Dx!$&HdlcZV>_RLe(abtV2wYqIyznOl#77Op+2By9b_iyop;4&3kE{@ zaA940c3xM%E_vK??-Q$rx-)xo5!Lvt$`~0Z%IncZ$U3JFtf87#sP9cKRXR%-GjaWB zE>zh?&fv7)IbV{SimhS~fs~i;Ec-^E&2`=OYICi!GAUVlKTdaL56cdh@QfX@O#h7% zGW{yMYOBgr5z9I%d)9&J<90j8yS4Rxwe)Hq_-s-Yu`DIW*qu(d4h^zj`C&2<_1fAu z;lskcr`ea+NBP3CGX94s+Fa{b@sO^#xn}JPbp*rf_r2EkY27gRSFhk>k^xFW>*-@pw zMBbuORh4abJEQydr$!Ih2l)TXF1HU3+rtMA96WHv<%bUJKQuag$Ubm*_`qm-|B)-N zdYZG}IkJD$8s2vR7YFv)`zP$QbJgCsown_`Wu=Bu$?)(n{;W*Kj@y=Pr&6hS9RKXI z_F2RG<^QgZ#>Bc1B7P`#UX-kq@sGt@{*!oXJikwBXwB92R=evfYR^(n-Fre$pPhtS z)ma2)ThAGjm$ZS^gXgBSwkBWSy2rZRw+&Ap?mDh}%BsjKnx<7=b={Q}tHutf+Uf_^ zE8V3@e%6F$!Ys#23T3bo! z@8oD#N93J0>#Vi3yj8ZD1iaNs(dt-it+}m6!&)C&Z?|fTQXHHfme=hH9a8O72P~t$ zc&_QL)UD32vN`08cT<#1i>t`{A3tI)r)#9*5!s9Ks*( zy}%IAB|_z!k3IZ`cfRS5Kk-!!m9PB8=RW*_ANi9{{%tUfJx}2<{?hV?@#Mqsi{BYA z{JjfdGayS!`lr8m$NM}=`T}UlWRk-@s2Nay5Da!W|LFq5DgEuI-|>4E zMF5aP(}5BJvg;$;cRi8@szc;Sxt280GxZPA5Xy(7^LDbC^KD4x2&(?r=dEQ!9$O$v zp9(Bc!9eBEfA`?GB_|l55d02d;7^(?|6Ei=4Q>+g_$;{dW0EJ^h8k!&xHC{q>3qMz z@4$T`RQSMcpyW|2w1H)IcX=XapMZYHYJlv~Hi=(zi@a1weV&O1VMI1x`-}M z;99>D%M;{YNh*f1TSu)_%8B^6Tp}(<6i1H)96b`|=#el-kMwYKybf{nh{@4?bc%Q! zUB31>dW0PMuoLOgsnCItP9@8DI+ZNLZbSPFxcXp+;iJ=_hw+;FBYc zMLzkjcE*<0!m-Gw{?(p{jtD?v!74`5krO~6d=jNC0mC9gjDZw`Js{Z`aU%*z7Ad8d z)BjGgP&fbzkrAK@@NIu4T40sxE}gLpY-}W=@#9%2r_F|2(4b*9Oykxt8*ap{X*OKV z*9LxIvP*{pv%!}Y8nF6xfFD{`uo_cV=E<|N6Of36w zZxRcM&#sSp8+25?>indIn{i05VBb0%GA8+zp&gOF=R{ z_&vbPye~-Tlzay8056gY){92vCsu|U-NEJAh;?rUX!QM~0?aA>YF07~LD{i5I^)Du z$D-0}=yfcb`s=Vxs{J&jBNH(RE4vhf15y)V46@w|nFtL`JTpiUR*e%&e@=EC)^Kzp z4pM`jOv@M)^_y1ur=+$VJuZ?kfADyOZv-h=ewltP%VLtElU7oaZEOTng}b5j+gNlW zMrj~jJ~!b@)6oGleak-W`M3r(VBaq==mEdM z@b*3uwt%QOrlqxfz7R=6$2dB)}R{y`1XzeOtio2T@yP=)+2ThB>yA5hKQZ!uOfCBnNj*T zI6u-mW|fX;TDK*wmu{Z0NV&Eeu^+NH2ETKNazi1DgY-%panCgF!<5SwH&d<-ScJ(upcC{es;aHmm$w0^3%c}-9-XBXw zVu69y6I4-%1c$(5!~iKAd<|rCu#RHE)))rdOJ|@>KahoSj~nD;!m^5k*<0x!p?t`| zlD}gV78`IcyRt0v!OEl3#({9MO4lz>T695V;w9n4OWzWwOZM%M6XNukZAApLIA$Zs z1@XE}V&O6CK_`JSX^OMM6S$nT4wg%z%Lbw4K%tkU`%L_3!E&<`QY!PfHIVZL+?s+V z!`FsjIfRN11zOH$qoz`+Um7SSm1GMFbS9quXDKx!pdA`?4la@NxgZtAr$5%XfLfn^9k zgdI7-|G=OB8Q=N72w9t#(AmQrz)Qgyl)^|!WpW0%B$P9#mp52Fyuk|b1vTe<^ype6w{u1`pX4w(lc>nP7Vc=<$G8+(>i|1liTN zHF(8Eh75Y?iL41htPnl1N|T-#_%i89mkH66eiq_7pr=6%qnDlrHjGE3AvDxpx(E=k zpIO{v5W_KT8pQC)Nr@CEdcrye-X$U@LUA;y z17c#B_xDVI0I#>#Cr0>mX=0RjcTbG*=dOuy{@gi{=Fe>tdoVcF)N3eb&^u@qCxG_y z?cN1zVxJTDWDxH57?Se1q&?|R!&rAM7nXBegTp)|@T?u34Xo+M;G4?PV-edAzIESBoMLUb1{YCUwIO(Dd!^GKBv|&9^I#Oirl_GONCW`C9FFpMX zF8c_HzK$PF^mjWU8F3eG4Hvn_+3Ysc+KTr+Bqdm>DJ58`N%meL zkM5<~tpmQl)IziTcNhQ-=mfs4(FDL8Q`^j-@|JtU2U|fCmNypY=T% zK!|&jyGb#6M0l|otr&eFY>7ua&s#quDRP4UZRRefofM^ej7Uw*2&}<#(?*mfj;CHo z1|Ow=i0lDX(r}RB7-@^+E@X>K^c<8M+h^u_VnA`|Sy+XkTi!%Y@SYHI>M|kR)Xxmm z1PG*4w?|jqY>N+cyuFAxu<`y*+-9)U&%ggX9J|pT^fE(!zytp0^oIQGk_~J5xm;I8 zNB*Gn3@3s{ zlW0_`TjH5wFB-iJD!kt)viK0?bL?9Lf_uuY=?QK!kKoggqB^>%DM>;BzKGjY)&bwX z03T-Y9VzO6lcF+8BwvI6JC^1JHUWil&!fcvBMaoI9N6DE0Z*E97-I>MJB4SM#_y;A z=WmS!3Fvfg#XNG7ND+ZvVbhY0QrU-Jg+dUB8DTzVggGV}=tx+aG zKzhWP3KQd1(uqF`0rM|Pk!0UNCJ-0!_~K_jP#tmD7^oe0snWr&6FY7Vm6F}I6KUT% z3bzA&cgRn8{wS;?E)6LiCQQnsLu3b7_+$|(BXBars0BS8KUxc#b7H3Hlf|u}1s%h! zsRcQFZD>IUB^WjKvAtp{|E@#rDIBXu-l1rNN@ww_@5Oz0Dk5BH9_` zR6P)XvW5Xm(HHNWn;<|flsokAjkGKyN}^nslnG|iX7mBcT6wh6!R%)_tJHHhu$ zofR_hRnZj>l!JN#|B+mRcL<6CTw5l}p#uTN#XR@vv+!8!5Vz2K8F<^L=^qWk#&oWl zI>gm{ZRikiul7y((0)h#+QT^M3T2h14#6tJ7$>XrFiw`y^%xjuD~ub=Fno*~^e{de z3l`WR=o8YGp$mrfqgfdLCwKfg@>uy&A8;{`%O^#v6mANQGBZKW-TCvbXglEydTjmAO>M*|C` zsv2M+#C-@0)3`UWFe$sTr6#LidsyfxK_M(;l_nPIG9DJnGWwZ;g#gPit#$<5LgLK# zm?&umvXh$bpa$$SdaZs8Y{0)a8i}75p1U6y>$^)7c4-{z!Q0rDcx!u>p2FLB9}~~R zTL3tOw-DVLZ|Qx=BxIxs8OF5MU|mgM1r|IDUhYHk4)lRJ3qkHf1a~s)VS*MXaGtC+ z{T_N->4hM=d$u6_96)^7jp#&_CLyiAs2(h_KK3Z%0+MiO7Gl63!HFQwi7U+@2D?Rf;#E z!h!x62TBb{8pr@>dIEDx$R=s~tIHASQM7g;R^<8@xc)`@Psn0mFoKl;@-&&jiKMlR zLJY%uP>!hhbi|KOJ8&y7&Vea;^j`Sd>22n^bT%8<=0hG7Lq;0C z&xWEDo8R_n^q`&r4#PLNx0yFOOQpdr*F6{pw3g>_!N&mkGyj7Li>f8;lmT~F3M`4o4|fhs~Di)MG@1EF&eK_!1VjbwoxNg zojFRnW09vr5DvjV%F^=F^qT@5P=3S3&qq*gq7d27+PR=5#waHg6O?#5y}t-9lmr6j z_MOZ^w9$ZUlrs3wyy~vE=NO8{m=fUD?8LFiU9`0Ah4c4jY`mjeG=+NL(mgH^!RPfT zJd5fPS_yS3eDovY+RhrtUE)&v5^)pi&B{>di7``;qI_4mUAr$(g7=Y@*BF6?6 zVctfe3t%@w62!mS%SgE(`T%*Db-^s7%?RbhfKCh63zf(HuFM|pqy@EnnO1vR87&OQ z%SX^z(x?s8A|E+vdF2oRe*kG7svv0ZAphY1*{ck2XYaDJ2infwQ<=Tan1esO6Y3q( zJoY-`6gvk@9xFW{4S0O=UCgE1hmFvn_9V)qaj+Mekcwp>7>$e%94tg|M{WS}pV*60 zLyN&B$0S#FI(tbLX>Z9$oV!|Jy@@7zNiQ5oyz~X|0G4vp?U&^(5>50-X~}NKo=(r$}{X5vS8I6RCyG>K(AQImU){@I?>P4%PKF0!;!r! z{ZG`(s>q5leX_?f9WDdiP<8-YK|mkN%H@V}Nnl7-c)(#Z(mtYuOuv@cgzo^P=mHW% zJ(v@Y`!g{tMjwkrgb8oRFM40;_M$H>k$zB#C3Hj00rQpA{zJg+34$7MLbyAoGpitl z)F9BeTVV@AhV;OK(h6|rXk~1V*{x2@gNpZ-9zI|R@gEH*x196^!AP%c#p29I$eW*z zA3t;lKWiXjvQ8+0=@@QJ5##W+;dVp12WQG#W%;Hdt?_9`uYT=`80oZSm8OVcm8OW% zWjqlh%jjo@h#BZGe8qd4VPr6J9qB^<4aHmc)lt{e-U_F^jm=W|4II;CHkB6o{WaM8Nk(dZrfL2-I zqxRj5!1x}OmAed>+2~lBg8Cs~Ny+GnNWWLI1DSjz9HwDSpcd9+=>?2Ncfj7ze}4g^ z#K%De9(>JB&*#7b4}b@_!=Q%YYi@%c#=pePwM~C#W>Xp10yz<23!hVR7)B0|lh_f{ z>ldVdC@#HwSgS!d9Nf;deGKnj;*QZMeAV#GLGdwMd{I;$qykGvTtc~s5_A#8QmjeR z3d{s%a7B@zlVqRNe@^R{Ub^*rVf`$0{daD z0)N4#@gWp`ez>1XT>3}756O_psNt=|5A<3l|By~RjuBb*4sHzu=JHY#fw{cYL|~8#_{3%e-d(XucO0__MDXZ$7amY@=?F`Sf8hIg;?Kiel=CNlE>;Lj5OQ=R{za-Xf4^MSYbGY zhqyzZj%9<7s0>c*JGNDhs4W>N#U#v7*!}$@-h3HWEue1tyEFAf4Kbq4Q9~p?gFOdP zL*!OaJCU)%pHMcaiWWj`p>iVRFtpN%kn5y0U3Apm4ZP9bb6Hn9o+CJLkjpy6G>3h3Fo?u@(nb+)2kE=Zcrvd z7*R^BDrV|Vu!>f$EtYBk;*}8d&@ubOD`Az9GinWh>R?ZnLF)I7-KE0WB#u1PB|0H& z5}A-~VTCd@4+op)(&>io&?KYA0e6l32d>L_`t{N_0}`H^;OfpecLszW0#8f{Sk}G!$Aco=nyE!BFBIOC~2WR z+#RKUjQ3dez(2ZEjowQS5(Bw1peTHKJkF% z%U>W_*qp+k@hrskLaTCnwy9D99!G6bMWfqW{p2upE&Ud)+1Vk@4tF8V&Q|i-&&Jx_ z)GDmWhLh+nu!)z?pmg@xz&7!EO)_GK7w^VAqU_g!{c74ApXBn{vnO*#00983vsbLi3@RKav|;zA7UZy zFz+7_6Ce^U#6`4{3voyJbP1su+}+J^5EtSi>d1w-d-!u3!X~OyXjL|-UC3OPae}Oe z-|Sn!0$lW1Z3#iA@&V9OnhR=UT#h^5XCdqu7Q&+NG5({M1=fdO2>Aac9YKO&LNx zFdngXtZ2{S2kUUN_&LDkxKXadb?}1)xDewGViGXdxndVG(OZryw{roAyydu()q#O? zIj$cty_@mMz!h;9Bcg#m&iJWm$uZh#T5>1U#B5f`b*RyM@97;f8IP5k&Ind&IwQP7 zo{B09c@F~}l~}o;cIF%GpnR%+G6v=4T0$b5TWwAO z@bX9@c_b6SvyhC5hZaF1K*{xp_)+T-WoRbqtw$WKV-;t19wLXsj~ zjEf)>Tm(6+*CQsJ5iWvEI4LfIge#DU91ksmjAPFc*{EIwIV=grYjl{6MxEgg${^Kn z&}a&c4ztlH7eOYt2r}w7Dr3+{cjY2Tbc>_`?~S%0kO9GTz=T``Dbpb|uagi$N}^f< z8E2}*3nZ_T8did9Arq9Y-y+Y;^jQnZAh3f9p2vtL=-Ai$sO%(^v`u)9z++e`YEYkp zTZ8%#q7G3X#u1`E_P?JWC83sq>DM0ZCAbWXRhqQNDoxtcWjxZ8W%RRv^56)>*b>HC zNENL`vg|+y=o8|g2lzjXs>k&25KidyFyRiU$gGc~19>Agt|9XHr=wy&Utq}O@mWYs zgU27kt*K$5(;*&@-kbRXKIsj4OMT^QkH-(GrM^QU9?vRG9{Z>6xKt6`vH^T}-BV*|SF@rDQFG|-bn43_w7MeFlDOXK5VvW$547P)(`vV~_M z(u78#=@PaWRggHQS8ZbGNd<&VxttyEk4$Kt4eC=A&K)jgq_7x;NM>o|kaAyekklyC zNkQ}^4eD0Q4!j%in7mqIHqs^e6>1z|#X)p3!E6{=Bg%Ur~SSY_pPO_I@wAF+Svj2X5(Kh{uP1KWL6z|C| ziudFf$zgf6lxX_-xmCmhLS?ll_~{vW%&iX-P&}8@Z(FQ)<`Wxe#L7nF_xdE->xwb zsz_3<5|P>^?n1E%DH(4<39U&V1F40)B_UM+GB~6PAOSz53Q*PR=Pl7JS-%dc0<1FZ zEeWdvx{RkuOBSr3g;fD*#F{&2pf8(^*ny7M$G}04_e33-CQVZLmSVm-+G{CNi3Oqv59MrmQ z-%cC!x^8;GhbO2vOA6K2Xb^-%!i?54V^nYkHC`XP20h-VR&u^`S912@Ti+Ry^H;Bo zj$o+~mmBFr__!%@RGVuyO=z&65-{L_=oGs|V3JeXq0K0(l!0OvKG3d@->$c9PF!eJL80&m6`e1gXX+;XQU z#^pz2aBV_zFwF(__!!&)TwjE9&Yf{)@JiU|BMYPS#iYHk(;g;#pSVv$FUd-T`w6$ zr?78u`p3vxu&6cYqciS5#JHjiVyCoNM4HG=2$jpeGiT%&YL}x-zhB}Vvdm84^b3Zf zZd0%`coW(p>`kOmguuvJ2t$8kOo( zCXTd!3~|u(^7|uC9=%|?Af*RJ`o5UfY4sk#4ubRtJQQEUdQyghI0W)xVu&A0K8`?i zN-v*8c!Q2pM7J5{hIbQjjFdE(h_?bkyC~uq>32&cM{*p$0o7nZ;qw`^W17tfGD{U% z#6cGEzGT6)x?%i*r^(`(xMW@^4thXFUw2}ZZ&4nkL}F#)Yn+^J4_-^&faN#vu`Yo3 z;diW;nW}p_hmj#TW)CCrZqS7F%`u;_5SsRIF%TYr9Mpb;ca30URzNJ78B?o@*yoak zBwMvv5FE)Aq$qWZSSVq2v*dr^w@`;y5T$tz$B#idiCctjE&XQlJ?`*^eE`LRP_W^5 zDCD24gydhY)i6(1O4t!r#ju&tK_2jj4yG@82Whu+xpG2xDhk%8dkg2j+6!T@tm88m zo~%J~uAjSb^D;RN5`goSzV|RP&GtF_@0u9n zoYPDkEENCh!owIO6#wvo%}-hzxknGbcz-m z|Js*+=MBI4(J%h|H?fg4TmQ$$-v8IH`;kBX#NXhat$+PbKli@ZzyDpo{Fk`b)nBVO zl15i<_MQbA#9KsPkVrTj58!Yk>Nf1_t8r_jXyDY^5FD`KO9&2F$YdTw^`ikC&?Wsc z=u#B(k$U@3kmzHs%u`BOcy*OFp5>x?@6BG5^#F4B zJS*Hgu{X$J_{PVRFbpqFP_mF; zInc@Rt-~i_RMHnc7##DN?KZ>M3rI>{E0=>ByjCxT^jK~G<&lG(244jKMaceqAacWx+1CN^w{Igz{WIT@`~<3ku0 z%nilHh>n=M`eEVt6q<0e6Ba#L?Lv@KNT@eh>-00%|}tFpMe7dLK++@8T+K&(dk zrm!)PTq2vn)N$KiZV`b`1V3R5DD4RDC66w>pIwTah(HHY8+%@(Hul8bI3uC9-yxp{|r&~W^05Xz>Xp6 zCyF1rR~sDe7@K+>g|iwZ03_uo(op)lNZgijCV}(IVqUC?d+vC2t2#$D?E?z>7!WE_ zE7aw{xq%sa8Yz9j;!iLzuX1WJ) zGB{UrL};etq)b@#2wnh`*n&+qC07{0Esv2!m>SE{JbVK=+$^3#ai&D`dc+y#�Ok z`pe{Dlw+#w*Hu~e_RFg5ILczbL2Q;%h}?yr@uGbve#X#O{2-O}1b$Ln{RZfA(GcPQ z1;jcmE{SvS6X)tT%NgPM&6YEYB|fYTdr%TI{x&PAe~!c%)f3qNT!bO+F>eiV^f<^O z_@IQn-T{9i!a>P_sjLF9V8Wi;NgA+cra}OFna>))o{KFBJuLLoupa^d;AVm7kc+bw z?1ewoz&{B?!Y>xUK4!umQ)h32z1+n-fPJi_Vb9+7!X8L`A=m@Vggs7`BkYG1>~UIl zAJ_x+F;@oc-xKBuaYY0=HHgDgR0z08(ubeUBz^i<@rtBR7i|Xk%~g^~KnC0)O76f3 z;wMo^d2)vj`Q!{&Hn;u7*&75P?#79!bcX9r#8QhP?w8oQa}IYQOtAic@7JI3>X+vP z;%=Nv<`Y5vB6WYL{-myd6SffySVDz=krj-8lcfEh@f%9gC!YYlV-A#+2g8nIQun|Y zuw>pP2>qnrfz&HQ&2Q#?U{L7~`^|v;MP(yob#g)s{^qkn)h=&kyEySpZWN%*OU{=X zWNIU5+v&&JKH|4aW~TQBfH$4yDQW>uQ&=nRFK^@`T6G_cSkt=WwR%3(>UQM)sNX&b zn)}#;bEAEx8ky}Azs&YEL(mY{`0ls=(;_hJHxGMcIsH*Ki$V6`=p*QYqFi`SCv3^M zOG8@JU}hEnJ}7wzIq(sAC2+N0GJmyeWk1oxRRM~{>&bapNGN)8CzAdIdzS$_Mi3tL zs$~RfCyB@jDWK6ucnzXz*KF_}weyu6kgod`!4yCV=#O;2;sIZBRE2n@2*wOF)~JPW zOo=;jsz(wbxyOszn-ffnA{QM6;RBYs>Dxk03-wesLAfqU*_*9#{EYh+hD7G{4^gDk zQR7$iq7Z!iiX6mXh$h2pnTRUo2#KP@E2m1?c5m&WDnD*8Lb!aias;RfRB0<+(%*&Q zMh7SBn8risXdmc7J-TH0XdkLThA#PgmewWvn^+ux0w1$%!s zVhR|@wRm7)P+mQ;{~)2DMUGu#9TgEf8jB~AL&K?&(XsLLp1u3_AGqxDgI63neB{cj zp5{zsu9ku?a*M#$_)u?2GRMW(c&e1ZUlM=A_)Ft&AO7~^?=t*dfxkoeJB+_0_`4E+ zPs5*szYPAa#@{vgyAgjk;cp6mS^Q1ouYkWI{^sy^GyZPD-_!B;4E#L{e~-bq zABNvTbwJ(XYwUHrwTxQh=NDmb91 z<*asTrGLrq9n20XC?I zCz|jtd;O7fbjdW~GbVrOfdTR8KZTkFD1b|z0H*Qz zRi7Po`2t9#zQU!siLL}hu=g`gRuG_BFf<(HirvG(*dh^bScf(ara#Vc&>72{xAG>H zdV3@|$=pN$jN3S{fK(-WC`uEYVsF1kcov>7Nrdh{?D;59uMx_`D%o&blQPtfaxvz@ zq8{vT3)c;dM2RlxN7xN{#sCuhJjh|sz=cjLW`3Zc$HeJ;@x;)D_TMll~!-Ke$r$5=mO>B6_A8uracb}A_xJ7)- z>-}4ZpNG;vE9+yP0UX3m43%*dpT)J@kHDtGQerxu!duF!_`x6H1H1I8>~bc0y@iwx zKKl##?EQF#g-vo#N7;cnyh2|q_(69*qWke7{KEYS$Q7*@^75E?BwG+weDxuKO!rPY zHRNa?5J*qcz55q5B+m`8OaC+I9h}Bq?|uj$2mN{{?m|6UQayT$DB19b+a+61Ds1U~ z-Fp?+r|JH^l_?iN2Vu=4p!Eo%RP=?G9zoB4od9Ku$0OMZZc2`;_h&QmB%UE0U_HSK zfD|Rs#<-Fb5&MLSp2XC1y}63SDIR7C-=vj(tK=m({T1gI-6FmK&@l$~d4dord!OYm zc+#iwO9(use?P@vfc#@w$(P}g#@1sv=;Z;yR9iR;ZV^AjPl%%LW($(&;Krf40cRQ# zZ`IpUR&V&A+98a{U@`XuTuD+N2^AT%=*TUCb87-gl)B6G5+dE-EcA-uEF6k;9JB9dI~?_j}lfHV7j<;3CsOkCDkbl8M4x* z5lranNcBy?1jBQ46t{+fb_BO(@O(dC8-YtT9zSsThKTQMlBt#YwHLUQE~sas?Z5r1 zxCOi9*b86%%;(z{kguYdF1Z~h8i zvuEGddUi#AW0f6|8lRBIYKp$Lt`qW0&G> z`10e+r5n5%j5AzC`GOr-6myYc|3QYOfkgDa0ey+{;3JwRDNN8}CIa*Z=16k$6!9NM z{&}d|Q8mBlHayr9aWD;q%82x#;}U1V@S)nMR?&a-R$@K3k(n3<3&B>Cl!w?(h8!P? zE<2`Dax+MOx42+IAihB40}AT#p+y|4f$u=e!^Ay)Q;0yqdBfCIpn3EJ^eek0@R@=o zx5bLXWbVTsBlEa?dbER!KvVw)ueODx6M5|$VMVTm0BF#rz`vT?KtmWBBfn$#pM z>48w6&?HDGlGME0EJLJ-H)hgJ;IrTmvwO+*+d|Y9}dW78k8G!c{N5w_((pzCb zwH8cAYo3yfwdP4TKef}a<|+5urp9xS$90j`17!$(mjNlD3%bj0lLw^UBmjy{T{Jog zT#)W<5$;ulz2ZOwk^@njQn<8MZ1xS)9rT74QmH^C%`Zdc%V$ZcXQMX?Lp6T-z+yin zuwM0mgZZO!kE)roNS4Xll6v!a>W~}1n{_0ut5Ge~tYOU0QFV!DuMVE6%M_=NMu>H5 z>enJ72`dK!h?K@jQBcv^4}s_jCrwA;SDb6~F~7i$k4M_^=M;Hp~TXvBb1=@HAv4md%~c4AZoM zZHB$R`4or`7|JFYwv3!1om)78E!u69q`61`1cei-hQ@%_fj!u&h_xfWJ0Z|la5R+Z z|20V%J!0z)Ix4LIZvT$~Mlf(WQlUo*9>BK3BehZ=iD?kZL842ioX8!h0f>f?J3#Ga zX)gHymWq57gXJnFa0UPw8=EHpA*tcA9G3_t(RdUW8SpaQgQR?p0Yw8a$Fa8_0f#E0 zr~+Jf8E{uX#@yTnMu*!jxSj;EoRVR=ZV`)AvMfeFwE6I8|L0m4|RpB0GehNoeB zf=8twdSwRnA#vhyhKLTZM-&J|Bb(fdaZXEzaRUy+1OY&gUwCu2Or@(+zs7- zZzi_jybecdsaQQWt-c>G<1cv>Pm$5+JoMnb*d{UYl1J|))bA&Qxc^=p=blViul-2m zx>{>>y|w19btk*$*4>-e+wPgBdp0*&saMv!ZhNxrR%+c=+qMpF^~$PrR!1#iCH{|} zi6HDEN_U8bm}o~59k)&8jQnwg3bIXsou@THV%Kv$hRLZ1gjLHsHg4qV>=OwC&-I zZf&yFXms4}E23+aRkw3_b8WI-Ik(b0xzwHPRL(TlPEK;d&PS~@e_sJmP1c*8^_9vw zIXEGEFF3@6el7v5AHM=44jLhlkC|ifs_YBwm{^$X1K+3>bK7m;r4Y@CI2}2#Qt5Oj zTkCGSLhPArRGKSpUD#F}DARH47N&Wm(*-uKPO5?yATiQhTi?JC<#QW|INI9inoqt(?7Cu~Shj-h?@fY1bisQ%DQkb! zI^4g!DtRDkP4$28Lr8u&Sy`)3HdZz|OX5J#yRBDaRP7}6kI5}b<9hkVvy!6((r5aVRr$p)B5r}+vh zS?PJUzwMsha64VMuINmDm|orJLehd!d;PM~MuUx&%E=DSVis#+41HxUCmRrXEdXG6 zz0zF*70EBg#9B07L#MiKFvcPGo*Ia(dB#n4+Z$^&yhJhZU$9o#wARueL&O73ENjTGO(Zag(KYvMP>*dLhHj*KtfVa~_NHhO%&-la zdZk#sMnfcZwzaWRpOm5W#m5|dCIlL*vp zwI+dlC*1?hl@<47Wo5E@4v5hq(yZ0o6vX{{yLA%ER|nTX>GlS6zdH1nGnJKQJ<@EA zG&=?!t+ZO}W2|(wvUX15cV`6f>r?e(B5)>*IAP+zMXG>`halkVzz_uM$IfZVM!5c|NW)CQ_sUvIV1`8b;B zcF&FBr?XL8@_N8e8^Cc{CA5XrW)~kv*PH9^5J3wqaOE6WkQ_K5nQ{?MMtgblYcR7z}=*);?$*ASBTJQl+yriOy}{%d63RfCN-u zF6%)gU$YTQ*HFAKi(|s^*!OjDrSuJ1*BeqPs8A{%yT44iMY-O zvxhA=N3F~IOf{(oUe0AG!A9XK$lGnt`Kb2vsCBSUVTmW=8KQYDDtM*|$7dOOr2!U6 zt3I{^H~RL8Gm6R7iuiR=inR_wac!SIC=nO%naN6*o-=XC?9Q))q4yg-<$-v%4>Uu7 zj_X!g@=%{txuSUu0Qg&ydgEIS&d?h6#3VJ1D#c)*_315OCiM(7a64-6aC(%b zg&@+ z=RF+_6?~V^3pb;R)GQ8cMebS`LLJ}TpRP96B!HyafUip1wB$_^$Z4_MptM>nu1@d}G=89!ge+np3N~3`;`X3G6;;U5{);4EC!upYAQ3Yy6A|0`A(;){3&^YC>*U=`VAbe9d^)?L0CmBxvwnxOm9(cXh z-0s;{`xM+!s|P%Laa2=V-Cb+Cb*XXs>S@|jF`<@ktO7u^RpEuxN^pW48c+7aK~mdj zgLl-<0Wxb17?fS9_L@4oVIhoq^-`?3X|if%Z9+|luDicduT#yRq~=_0u1Vu;%zKDB zp^NS%4}^KJu?8UvDu!ac7fr7LC2174F;)uVX>?2%}5{mcyiDMYP3&2@ON z)leIi)#eIpDqFOo_u+qZ7vM|pJy1uqlCRYNQ!fj_%LHswS+j^+vT~6G+71Wx+tv^b zI$9mCOz1Y_P}yZ08ut3JZKXszsy-c}6#_H9(K_dqN+vrsAlhWJ4lg;&7reRi%a8iK z8PUBNLvJ|R-j>EaC#!nVIj%9Zuj6GD1eEY_7~XqQ58OK56Z*61q=sV!9$@9wuDP&a zB+d)l7CA{XUQ@ka^lfvTZ5rm2ybZw0+s(5m7Or65dYtW>x+@($@Xs05+sfMmS`Ez| zTuu?Ow?D$bgLIk3I}aZWgKC(*hh>EMW1g9ivf#z_S!4f_V2wn40uNTDo>wtu_+C@i zc+@)PF<9?&{iVlX0}o?bEZO4JkZ2C0GyB+?s5fnUlz+h|Z$$`uVRXZFQzwB}&VbNJ zgy-ph-P6+;*x>;$?BGxB`lO^_xByOVtoNPRecsfCQBo}2Mp$2@w=O)>(qR`uOgVqO zGc5xh98e#+kinWQCVWJ!+6nj*T)4HMt*+IhRS0Z&a5_sH-8wvg+7X2w40mfD&4K4H zVHSW47Hl7FHtq2yVecq(0C-&V1g5NghPyPVWD5Mctyr?y?;+G4e(b9}#>b83S`&T% zGH`mim8BLmIi@+d*`}{I7}cmYH6s_75eZ@8w<4Dx1TI~{!6>P}$AWj*S6GY+bU(Zr z4jUeE<15t+qi*h+fFHkKS9DF@N1~Y)E&GV<$dzVCt}<+VqX|md^{#jt`kgXAclx&$ zni4V5e{f+SiirRkAoG6E@Mu)gnaH3s=grQ*U*j7FM&Bi)r`rk2o;GCm#H5SoJmuVP7n7mcgR!5^QD>%{SuJbsGE{$l==ZA z6OcoRi7`(yjfW*uf-zs;@eovEN~S#?4;%{xh>ed;$+UNKlf9j1H9mtgstHRbprp@l zjgNYy&t9=U0xZ}c#FxiXtCf3bRcx$uJ7+D39^YGKRHG>r@-Gxy9~<((+SzYiZg~BS zqNGAWVVMZDz%lEJ{$mOY9aEwjHTDRdLjk~n#{GcMBXka=$c;6 zi?8}Jse|CWBL*M1betEcZPa@aoHrQIX3ooZ7v{W&m{kTo2=|f9r-9v@ruRlg*JMPq zBczdvfZ3SwW}`5}WU^}|w^v^uS3iA$cX)FH@OKRBdf|;@#VV17-9^QC*0<(~i>evk&`mYtS>Kv7VgYlAm^32SYnTq1 zNoE6#H~Iq(zw2Ae)v}gx|4WPmS#PXHIX^kPo|k>=ISNK|Sx;xgf>dQ-3-r9Ol^5w$ zL?hc?0@iOu&0MtaK(?O3Ytezrd&i-B0K=j8O>Y%@ij6U>%!Zx#g>s&V6ii; zp_9;Q8y%9Q4TF%JQqeYUP9pE{xc==s|2K$*`=7W|N1PtEF%e*RONb%kXT8)u9N7k0`E8{DogTcgAq9#SuX*+t`OI~s(Ui0V0 z;mbXV#fCTAo#I=9W7E9mT)J48P89ek?D!&X_IGLehlr-6(oTcL;I)H%_% zJx{v~w5Jze+SmAX^d)G9iNVG|PeW{AEBEWEyTTAMfRA3gEz58WHQh zx|>nnQD)KX^^9!wF<!uP@G&-vYfo@x~)i);JVd}Y59 zDi=mvhw;hidB%d4eOXVndf@bbcwYeo`+L4Z8Z`3TuNO<>-e)&hkf**s#(=TJz$ryP z;zp?hsETi(U%}VZHKOeY&0^+CEDxAw^k1!J#+(4Z0ATMMrJD4@J zU!ZpgSFVa%Me_bL!=Lk0ggD)3!sy%mtR+=6%w5DzR>!DRN;NVhD&O| zexF!6BAS;2M1M>WEsHOq=~I8=1L}*G^``)op8~iQub13-(Ze8=;{5oQ``WeYl# z31;aj8gPILf#d_YfS1KEyiq42h+|1Up7gY(Avy=+$ZD>yxLCU^rli3jVdBr9dfVUf z$VSCgD6&fJ;%1gT4wVJbc_=>jAh1(M7Of>pWm?a>Yrl5aetAp6UO|tMzrbvF?bpk9 z*M3Q1zPt9zUwEj5^yKc^uidp@yKBF8*M9A;{nG2fcGrIGuKn6w`^5yU-L+pYH!$Q-{mTt^6uI%Y~*1;hq*VqYroV|=iRkm!FCu1iug;!cGrHr z+*>wvPOa(pmL`O`vwF9?_6y6ccGrIGuKn6w`z1GP+gsgS{()JlYb{;VBsA5 z-L+p>OYU~3%nbY8wO;_p?%FTzGN_k~?XLZjE9Z9CeyMFLg9T4U=F0Bcuidp@yKBFs zFx_4IrB;FNuKl8FwY&CfyH&TlYrl5ae(kRP;(qkIYrnAJ{_ff@9y{>lulT}$M%8YYEX+_X6vo^klt`O&iyL6&=ngW z>>gbpm*?#sU9fv}!S2xo*nna8=mNPabob~2d1{rB$;6#6c8@NQXYXK7*4?8EyiEzN z>$7S2?$HH|u=at>?$HG}PhPw-Gl}Hm-^^})Dz#CZN%PWsbcppD{7u>$8AGwoN?wKSB6YsJ{gny&I!>;TiEYD zg^etkLJW^pM_`{(iyJhisHJ!h_j1=4wUL}??c;{!Y30k%AK;JydE}$0kMpR`%hxM7 z=b*AS+2rv8+@J};-WEJIt=_4%`m9rI zk>o$ZYk!}I-Lp>5^0Z0+s1?)qGPQfwsajXR%}J`eXPy4rIqNj?)JJA`KC}q=AN97C z(amnjC$i5bHrcd9*rBN#>f`8qf;(>}E~;kkzSz5`=T@68Ci{yGy$KHXa4cQBD>fi^ z{Vl}heu%qAWP3YT?H-YBoUCqSqwXG&eKAL5?}`mWG%zqVRKkUzTqG7x`XCfm?xh|jYDry zEYASLM$4PKhEr)+0EakjK3@)dGw+@OwtEJccYqEeL-Ht0jm5iXfO(rro9l|;qT4+K z%oV$5fKBD6vXkxFWImgpp3KRG-q-*ahkORuqEDg;v2VE(ZZG?mjDHIjEhPXB$u#PP z5P0&=088sLz`}A#4kI)$IfmYW$5 zaE#>c8DL!qZh4Ap=s3lU&}Sa%)o$O>XMpVi=N&T6>bi8C7i+|fdM|?W24wqq&j8z} zy}KbcYj}(=)!EehX}%ceMC)UxA3wuK61q{HHd(gf^t;DSKb~W!FGE0uc3saTF)2EM zgVMT{HN>1F3@qcIGkdLdR=^A$hHIj=vcdCjaM~GS?BEb*m)tcwS*gkwI2jvB+C#x< z0yz8ZxZF7(2ddOgb&jvL)^H{e4x^ov5BeUHba_-<*C*oa?Vm4?Owk8yN296Bh1e7R z8Xq7GY^6umacXbnq&rz})jHNI@3WpegcFJC8#SCGBt~#qb2}Xbx~u0RqbLJQ7@_Yq zoDUPvPUQ+y*)$*W(CCvm5vaYgjHSq z{_oDqV^(!ZR z-OW|1GsRlH9Ca_ch28=6X{L}br&lh~ z9LQF4QGRxZ+ItK<82+Lbh{mb)~s>s!yZENQ4Mysjj$>k7{RT-057gFq?IY;48Ck z`S5m-&gIzVB22DUqmiFmJi1Y=9+m&Vxai;%R9x5jpnf!0ke{wJJBEo-nVzlW8d>OR z^TnCDY`%P?xpO2^=ZhEAPp&|vOkFR^vtN5qC|7W6wQ9Bj`8^BHlq+9(>o!fzz8D$X zuC&AQwMw`xmM#6rXH$IyW~vUj#}P^X034@PZZPX4yOA;P8CD=Rsh0x!GcV z&dtu{=cP*j0QzNLu_$X$}S6?#OL#&aD7<(XHnRB7)xY_JX4Tf#CSU!5m znxyF=k5K4ovRcp2!0gDvZinJi$(66!j#FF=T1iE?r?uLAZLT<*L!`}}p3gO^v$D_fi2iN9Namn2q_FJe&@5Gx^z^TLJgE>CB~SbsEQ1!x!1D zEG|_#OFejtiJYy27-w>`@KMgrmM1SA!wt2&8PK(gTb!-AGj6>xJ6D;ll&4-#Lu4!G z=Y{w|KHJavl!v#{w2>iF;h5?L!u)*Aop&3#;@oUw2I|}K_ehf{e{q&Fc0Ge5%x&Y8 z`pU|pTzNA!^%8wAHM_^BVdbZ1ixn`k+2VYz2Kp;!e^PZXe^EAbxO+Iz7N&gAKc(kB z&JNC3i`CkkJ6mxf#OB;`?k%!|+4+m=p!V+rhP8EcthWAQxt;gp>r-WJEdAn!pvu<&|o}YK~+1hk=x;*{T9iuN>m~roE zcI7`k=m<-2I$wl+2L3ZMTbnJR26w(3q7 z8Z&k1<MMGa^;(M?wj7Ko!qpy#}>__dJke& z;VPb)p3OmP%Vn#TV)>Sxi+Z!gkjFn%maVB4%E4tP4({~;j z+p@vFe()UJ5c%mwtzKyWLua$)XYAaywPj1trWs&GuuhF`5hsm9*WcJAei=G{d31;O^oJr7E#}=DeV{F&27)UVC9>92^holJkSw- zc!VC&P&Qk>eUDeE-_G+!Wxy8$lFdGUOx@D7W6LiXYSa<*W_Y1E}z=C03ru2-7hQK{rB0|yD7nwx1`l)j1n{s zf4s%sNXp4Q^07JhOV~gGU;2;yd2SBL7c}*j)g^7sQ={@JXOVAKR6D(qiPe<;iWsnl zd=|8zo%yY_yfeJ)>nX#XdYY*&wMD9x?O}NdxOd!6ssj;NUp&`zSL)qi?ePTaZltJj zFRH^~&J4*LgzwLsRhJ!it$xp_eA-#s=+;|j*Us<{9B}Jko8rwYj+(LS68_XRGB` z>^yT~dl#Zqk(5UZY;3Lyhwv<1E`@@dtz^ruykyPzlZnyPr>CLqBQ*d9Ykda3qiP^&@Tf?szIHVb%hp?rUB8)CVTwQa_s zeGgS}X%6{CE~Mh@Ol2ltK?=qBoxA6^2UL$&3JD@eDX7n9XR>haNx~ZvbnkZ%ya=(>ubdFCBlPTkEaRb35~ZgpND_BFkf-!vgHTGrAjx+6*jq-ic9m2 z`8iBdu~w_pW}rKjUn^eu1O|6eE+z0hWSvY`=MgxWsTb=rv*p)`r*G4GVYO0gw>q=y z*wBXrT1EE3Tov(_YO&UsuNTU%7mv^43fVu~7zya{+)TB>;3XoLb9qFL9unJWa=OR4 ztSkGxxG|01E+K&l&V(6cujOmm8ZxzhXxmBdSIKNpA)`<$WVuL>%*`TqWoFi`&%^wj zT3I=>I+dF)%pl!wb|#;L**5+E6>}y%QX5empBg1F7X*s9M&ghI5uxRB?PpY6B7#DI z14L@M>@pe2Js$P+5apILWFdRPnmv#W_#`>z>*W9J?wNK^Xog0pPu(+amtQR|zxRK? zXD(HeTw~YCe{rq*(mEdXCIPFx-r44psJ8Qjm`!^q4ad2Mv4rq6M7jn+)rF1s@oo21 zoX!0kxh?!hOEsrJege)_0nP;v zALabV>_)2%w!lw2Tkw0gD~OSx0rA-m`Kj0Z3!1Ssq?v%qE}}v3;e!8U`8H|9U{*Ux z0Bxoe_n$7`CS!95O&U)?qs!7te`EPJIV8Mjg2+~w#vbpXr#$;11tX}`*&`a zbeq0;fnC}KtLn|N@c)DvLbwD&T8nq zssHvF{O;*Kbey5C7z~#yI!MhBia_Upybu06_U=NR>0D(;T7aXX6(&^02p>k2{w7;Y ztM4?wM|g#XQNn(nK+D#6u4U5SVjt42A?Yp_5Ky%xwjMdj{$`bdwx2`!Pibk;d3KRb za(^IB1_f1(Vmt%-fQyM+#vQl-6k>2xw7@HqspYrXg*ju{sbw+mjG&4P#xN2IBojOc zt2vEPx=d)~1}u07e}`Rs=5`y_s|A2sg^c!-G>HM+4WKoJ^tvE@R-xXWwIQE!3W*zz_!VLYrs>LbZss8M*(Son5Fw)hMadWe1TYS}WkFDwJ3F zAK1BtTC@{cj+R7j@rcP%S6FDRQu`m-JY1|Z@ttvOvtOj9^&7n%+GS1BKWJqjk2;Tv z2bCc(8ixY^v*vzcZ!gpz138S~U>YNyXTzlGzedJF$YB77$&5p zQ41E0^}n#o3mM$fi!&=aDUp2V(WRk4@>>c2D?3CTBLyh-u?_sj4mAq(1VV5{+)5y$~mPY$3;BpaO6T=SN&?eY&Bc~fFfe+Haf$q^?xu$=NKxJ z!^95zVSBwAq1X^w!2lurAm99erWhqR#95*qi3&HQ|C8w_pJ#C1puURCB$6d5M$)0q zLOT;L~|IH4dRUCZSe1Tby*un929<#MR@(Qq99JF{( zZ=Z$<%IRi}$UP3(E6oQ%A3GzxdSrI=R?h5JBCf7gcp3IK>J-^a=TKz0(y2^jFH_-R z{wnVoR308Xp642MTvFR(lh;Nh;`Nm`4v&x)!ZejJ8v&f%T0L`hTu2yWWDqt!n>w;)YNorZ zr>c5JgC)&?WGtJ*;6fg+H&&vf$_Idy8$n3oNkM#^8tV#eTfs zFCsECvig{jG$k3f5j~X|5gF(I{{JJws`pofWm)18`_QY!1?$z8xc}Am1w1$x?#I9J z3m4>X48M3y@rVDjUY%4AcuJ@z>(%!C7sLhBf`1%Y1_{=y<)=up@E5;u|NXii{L04| zlT<^@$;it5Ojd)>&>>l;tY3bhUhkKA{8#l856G2;nIIozKhXs8{4V1$EwkAfyyyYo z3GLl4dwf5B*jfDAADAjQ-s;)Jinr43owqIPs&n;z=ho(OXSPxAdq>xLUU$t~?N6QX z2D7bB&ub34y^hzntb@8>w|TmMbfvr69duVa&DrKM62^L7zq`CX=yX>t;nye&o~th} zZ!EKIWzK5%+?KV-OzVK`OS`k|_0RVQ-pbMN@nEahT?=}Ce%4zZ^v=)r2R*O8Vr}XyAt{V7h&;QX4meW^Z5^*gWeEN7d#;^%sufoH|GDKd$Ex5D9U+h~j< z*tW!;CaN2FVSTgpffe7T`Q&nE^)w45wkfoy2Vgq;Bx+6fyw&=O$9j|7)H~Kczv8!_ z+NRv(`l>8OAdmyv4ph_q^+vzh>ohzoJte`Y(>4Pjt5XgH~rX&AF3L*5uv|`IwI}T>y`pmGF8! zzXTqwNd*?+ir&*1#60}7aeiVPyyXlp)cJutnAYW0X?dCU8Z=LP*J!@(k ztdtYbUap_$Z%dNWq?Cis3V=GhO>JsWyS6LQAAp{0N4k?NK?1RRn`*{`{;=epZAvD| zC|PJb+L+;F`$TNJDcvhU*5|mIZ&|j}IXN-8H#4!%-pBu6b+x^J%pTjfZ~wk)u0F7D z?}3Q}_P&E-`zH2Yd)@WVare5{?wzp4CimfC-=qchZjad+E3+>X&tx+D#>TAa>3IBV zd+&_B+fBsnNsIsO<^OKj8;grIAwc(<6yf0A!a%KDNt$3rpNKAtem7p}u&*q%+B^0}Q+u5g zCqV`8oMJLX5C8KQ%YuFiE0CQ3TlSd+nXJJ4CF~aDEFkH(PO8VCs+Y1S$dd^Gh9&Yz z>)SZQHHDThOX696eAk$MLOJW(IY6-0^_7O#vtGeMea|~>-7TLe7rc^B18;fR`VMwL z)%KnH*6M?kOG+$Q3ZTz=)h?MH8RmCg9Zd^I^xbKhLom0#M?DiatnZzW={^cr-=|tr z;)2oI_h)oDIohRh`KFn__3EsARXko!yj7PRb4hc|MtZH+Z7xY-*c_A3y*jB=E7NbD z^jb^jJKl23^2QVgl`G!P^g#@l>dmINHn2{lYiJ* zPK_-|AX-AE6U!_uo%HH!OO%_P_}HVr;hcPYOo$6NT*UvZYVPz*?2vr4kNf|*)`aDX z2`l>{7MZaQ+t!SzehvRizHCCc$g7G|CEIe5ldAsx@BP#p>^mlG`?zaW|K)4WDHQh; zEt$BNNi(9xT3y?9vcJfBr|t1XhEEAC;9vLw zKDMe^*2Q!Yq`w690^v*cI4g`*-CM1r`1#@={_6V^CF>S9w)p9f{NMw>`+;};s#~^h z!0X?C;T`XJ{d*tzHM}0g>t{akjtBnmjsNX~|HCR+bSDn|9S5*>uUUd?oWT?ZOM|X?#qAq2M>JZscJeUs}O1p7}X^T{wr&eB%#j@iE=6_~NIKgtwPvCJcOgr+(^ zO?5%GPLay})6Y4YxCsz9kh0UReQL(8B3%+N*DkS)>xAu!sSI8vEpS3|=0wSv6D4QP z5IIA3gq%4hIeW-o1IH(4GTSF-jwWYLNY0$aC^?hmeRAfgX_aXqIs31Uunle#%nVrr zXgP^D*vNE@GgY!vZmb3#Gty!I&LxJu_?0IfeA|Eh*q=QNAxBk%Yli_8eXp^-u{BiZ%w z?YnMg0qS7BlEbD9@XS1%h=BZ%7_o^ELD`{tVT0g+#38Z4LDH}MzIDnF7VxPA7cF@h z*hwrIDbxRoKfha$jgdC~=8)QNO0!1V_>YE-e?7CBho7lo{BvBy4N_n_*K1MUBFLB3&l>rBG*=Nla z?SMN~E#RH4xRV`nCp*fW>?n7#hqx26BizY0xly;H!#}(PAqoyV5kWOOjl2= z&JN*2GhJ3=GF@HHXSzNdvFc&a9#z)^@n?&!o8k{=#T0)y98>(!!|}zREr*jjMM{_b zs$4#-+BnFJBwQ&;gh-2GnBXTYEpul+6&Eodo?xRe!0AepQb|6g_^Bu=I#ji$EdZq? zt|X$hlqy9!!ZAgD}eEm(5WF>UOAy7!610t-&PPh<)A! z`?W`KNPD{i;x-{${pVVfwk=>Vx5=ZPj~}g@Rj?cekILcI(9I6x)zr;gW*hWqKPuWE z>Slj!2dox?m(C2et1J-xqBR|tDk#3ozD{W+5a(U{W@rDRwRrLEfAvXc(aOF{(uCW9 zW5~!ZS}&&_db5+7NpJ+Ginev{Ox#V}JppYNCX})x5p{#jBv5PIm~S*0)!3s(lMQ1Z z?ZgioO?302QjF!`TAIE^uBB--(ceUjCeQFNhGm?Q4dY$OVZ<}MBeukuVYXz`WS6pwR9w`~t?ISYaUW-}UU7JCwOp7O!a)jd3I~y6;9z0{4gw?-0PyUG z3G3C=bes?xn@&<1WTmO2;$qIVUJ98s1yL`zd`d>-Kt?6jR&PEvYe}*7YvRmIs%oQ4 z)!zYkb5m7u2l$e#(W+i6i#acn@+IWX!}fk~EC%B|D0v*ecB(fbA4&hOM8kN-O}a^R z!5@grLHej;c;HBSoHL_#!v*N`Y`Eb9^w}!#2b}HeAZjK+pHUCv4TjVI(Ww2FhN7&r z^}`jMg@U#J)U*Ro7S*4~$=ERjlR+&a%@;5q;U9VdvVW^sfy}5rl2b0N1FY;Pa#j09 z6EOHwTg$><0FB6n8-SHO>Z?4y-1R&pYMGOgh{4&UP#M}q!1N=WEsJ-Pv!zr|{K&A+ z_&!Ey?9-VcXG38KyrXLT^V>K6BT-rwkufy+ii!|0?giw_6d~vu91@U0<@b#le4T1tALd^}7TEeAg%!et|FmS?LdddlgNh}~tk|F9$W9g+(F{;J` z!n9Qgk0eY{zRvNOd|h`r!q=bSVFZL})WdkafeX}h#U?&_m}D51WUE*eyaoIl9CITA zkW@(uW>^14g}=I%B4HoF>kLtgOkLTNl*7LIF;y`8gQRNE9YMFicYj;bIFPix)AW9l z0)ke$R`wrN#!)c_JC8Uj7%ru~OiIW=bO3p?dJ(2nTLm&7^cl1u6}lZ&2QE=zlE8Ua zn0RmtUQIl>f!T)IeGnBHF3})VMbO`&GYvc#Y2!htictP~-MlZ~$}Vv{rhLosn0TPS z@$ul<7)B7PGU{RcZ^xj&v^|d;Erb5b_Kp9%L6jgY88k_=XCn8E<7@aJP2fNp@lEPv zALoA?X%uJ%5)CFDBVmb=$gtwL&!BB^R8zySQQ44&5%(Filo0NiaHwF=1RW?CwpK3K z-s=_Jnzd3BFr>q-4Jl2Mf#-!Z=rFM`4A6H;WI|pHEhR#OATUN~Pzn{LLepQsM>PKE z>AaCU0o*&n3t?b-k(NC^0W&VGhb zveeFL($4;nL?Gl`LxTWud)C?y9o~@wF_iYI195oqYhU~CU$drDix>apzuz{Ume#=( zw0B>TA5Uc#FD%|inf}bdnY5c;{LKpwus3H8PNz}q|6?nt5&37UsWZ$ui_9J$2GuMc ztmL>&Al#8}*n1 zB$eu-9+5#^MmnJZl8$^rt)iV;6tAC2FFs*C=%xWia{m^fjxbuar?FuxJq=J5ZVI7* z_(_)?kSy0aCa@dKO(2#8C9xXXZmZEY8j>2?aFr!0^J7tdYAYEZ#ydyR1>ljW{x8tt zz(YuN1>WiU6l9#vD=vYa>HVNQMd%pwxCz zI$)$0UJld#APi?N;k%%!sqO@++su^z^!pZ}>P0Bn`CR2zEZbfRI4y-#p4m zk%mz<9!ULP-$6Bj)IaKB{Fi8e`ZHw-+R6?Dq^lulomAkmOgRbPMKdaENX1FI*!F|h31?7Vt7CIR0;C`HcN zCONLmxhOVf744JLN!Kpg_e{e?U9=w(kpECiWqOQ1$EVSuo1MF+$NBTB=?R#glx#>1 za-!@&IdjL$4qZpqtrq!pM)B)0I9Q=aT!3hKf#16P=P&{$4&FO6=8n1PyAPI~iY$>* zC16jN9$l7zMCztkA}32ERSAq84qa783cjx-OB|*+FwTjawJL$Q=12Q~Z^-0c6%|46 z!mFWH-icS!v<1OBqETi6tQhuY;48aeJ zCa1D$%lU<-;5+=|kObf@hfI+Dal*-g?S-O;g$|{Mu&&ikn^O9yrYIntTMTTZjSow- zBU_(e*i^E#k3ZQcDSoj9!P!LBj45hkT`skeTAC6p>9JPArL@$}QF*O~kxDzwB1p9#NMs@vy>KQ{>2D%PrPB&sdx;w8vBC4pN=y8 zefVBewg?MKMWupCK6ze1EUByC#%I93@Whr z(6AxSJT@&DS|n6_-^H2KV)h|0z|{M|{-A-XNa5tYsm!9&fFK^Mwbn%Kfu zN;{Ju>j4{-sX^l7{V?pr-1G~?A{qxzn>+<3?LKsypqDpOvhT#t*fD`<6IYN&$K3H3 zhyy5_m1W0+vXdp-Mef8gu^&G+^mXuJ+Pqk;ET4US5KEF!u_Ou2qLV`=QZ6$hNtEQD zpszkwg_mC`>Tc5KSTVwX0eM<;&EcE}1O5;5w3bj%Meic;k8VcbKPx*KF!ccWWI&0| zG^oZ%8xN3AM`GyaedLo};&@Er$MKlNPk-YRzh`3@0SzDZFdm%@cT3T48&{yBXovZ) zYXeyv8hB!qsV87YBJK(>IqcK386FR?rKyMU{lHz*#S#!66b}dwY&!wrfmF}`P}xu% z1f*ZpATWYX70B1H=#sB~n0ysRBDuvnMk_0_XVSjW*p_Co&_oo~8zd)B`#u+#$B5{X zlZz-Px2y7$r3nNktKF3#K5~!j1mu0{3P~av$(UmL$;aSt{vo0(vgK~tILy!NLfjab{yepDQ@B=~&x9e?}IH-6-ocodcfm5e?_<=I$<4_-$9iDzGV z;-UBd&yW4d2fu=D6Dy+oy5mFpx+PXz^DGYQt8pDGzRj{+8RQ-*bp6XSHH^R5bjLis z6N+b!Y14K})gRq4Fe1HgKgcv;Q^5C5V5QcW5RVf7r5Hf1t08kWi z=cwF70j(MJFn%~0YCQ-Hiw`S3L&^?z|8B1CkVL8<CE8ZFZVyK&$;xE)LlT1HIKTKX6b zs|Y?B2dk`0}nV}fe~bO)p;@A#2sg5rzdDCR~@e>hgu#N0q-(q=22?PG3WeKS=iHesqvu)al9CS8ZGGRZo0nt{o| zKwry&G8&e~m+Ez#by{AqgAaR|XjDb8e>FScZwfXSxqOHNdj&#xka=Fc+Q_?Tdh4gGw6P7XvCe($NQ0a;rWoSI=nRJ)n}KpZZY_Be2wL zGmPE9bZMy}etz1p)M(K0Ej7=^GQ2LSO&Z(u{|aHiD3{@ZXKEPlop8p%bAy8=A4QDM z1{pYHz2=izROLP{?pc&^t7E8x z!Zb3tjcXcj)AT8T10pH11q@-XNcw%0A`N!FeI|2odK`3#RZU1?(oF>pf=SBG<6zYI zNzF|84uXt3r5W`UKC4ZThEGL3fmucOv!wgu8R(^xD52v>@0g%n8mt&!?^h8c)Ab+eLT=*s;AO`(8Z3w{Q=Bc9oqq2($=U zIaw9cyItq5iG+RJ!Kae>SHFObyMO4{hI1P3f^UY(Fc>`RKWyiafZd1}?wxUvC&tt8 z&WLHWc~tD5p5*iH@uW@yd(;B(q)taO5G@P%3H*Z*@%}L7chf%@74IpVo8moWb98%4 z@m^>9LV}cbT~uN6D^_WW_xc;3U&(KDnjsX7bQl5Sc{Umk7>^duM>vN7Rv$b7GFVEX zp>|>ukaZn`tTnwGfn+txTh)*JD^Pilf&ifXlxt53h7l#5X2_tRd15@f9ULa$F~6B*Hk6a?IbGn&6?PplX_sNo?it64g(DuJNmf5I4a0 zPs#TxBG2?_6V)%Mq(_3JJs49e`v`?!#2CPQJb4HNLl=3OsW+m(4WWpRRuuX_?DM-8b zhgIsLIHvOnz`Lv*Wi*45jT4B4Tw1+|><3b~xZpoCQln#!koH-Xruw*FX?xV&HW`RM zY&C^{uQKtnw1Ir%IotlAn8^@?fM1+CnqRTGP z`_75kn|+h?bAyke6egTurHK8xA52HC!6J4h$~L$X!U7Z^V9X~bLUu|qe9INb1U9rx zW5=HyqXodEyJJ7MZymN>U>z#Yazn_hbtv3d?D{s2~Kaw}G8ax49f&#mM)I?do#BOOK{ z;%gtmX2U3AUDy}#pN;+pmW6Hl&xJNE3tSe{vcO@PhivO%`Idz(hP4st z{1g6x^fLNGVlB5+0i=Pg0s1XM1anh&A-Wo**AC;bG9QU=d8oT>UT8oWPVJnEY^X%y zP?QXD0O&3^Q~tIzEdgufMoQu;o^jZLE3*HjvZ%!i*XD5t7@RuZLkaKE?Y31j>H>oc zjysT$I;vyTqZ^-ap5!xmhWqfwC*6Dm+W|HaY`+2TCbsKuBG~>64`X=a)5wPL!6f#I z1pN6^!@;KkG=r^NPWY*P9BgDm2r7Y8x+OkL_=Pt_8turYU@tWe@`CyV3=B=Laa1<6 z%|1NHD@kMD9TN%evrp36cP*n^<0{$}AKH_SV+jbn*g5bD*TU8d1ZF|jKseSb`UnSO z@=(-4(POdxRi015hb3ftvFNw?;7o%q0>#C-o=)VwqU=Q$yju znQz!rG2S5A8$|S-Q!y@rX8KM_p=_3;IhZ1_f_y`tit*+xwI$Fn)%o!~|mBR!;k9WTcesU=*}q#?~?Y zxZY>;xEh86g27odIMTZ~?K|-$LKt@@^K%_BZulK+)z+|%gWie29h-JcGDN zHA*_}zan{zcHB#My$G>zChdESm7M8|m?)8^e;)drtO<^NH=cbRttT9zk@kR0^RHX} z`j-RgZO4WMP#LwLaLCKI9jWl?ONGN=BQ1JVDB!l@-a4oqsp&C7Ck_S{UzpMhc$qc@3@=9yDQoSc73>d?s;Ho9$Uh9D4lj~wxw zR1*U?rrZcrf_51&O-z;A-6Jq!X=Hh$Bm<5wY9?r%b(EYH9EQqczauemk;71N=y${n znZ>*5G=p%Dfo*h}>Ci}>?HeZiGs7c>307$uCiFMHVM2bR(+oz7LHV0{FZ=uuU(_7w z6a*5&RvjPZ{09=ksOSHWHk`f@-+(oj?R}S6vxx^jj?j5HmJGx}cp?2}(n+3s3cKin z*wPYwZxO_a%ab_h1P9SMLj=nHFu5Gv;QBNU=>xxclhZhqyL+?KIO6oqz(;lw7MAF> zB5s_gag6cjo@uze-?;Ev-D-@5!qpT;raPPpUhG!B-)!EnrkUp6?6W858gGwL)B zmKakdFb~0L92qy`j`^o?q)1aROdc%}IO1PLKX@2tdlx0&;Rhq1dRhl$Kf8)~>`HfC9XbP6vKU9S0=WTWRJ-QbjuaWYJ%1GxR zASzq+e3T0i5S39c!0&ExQa~~!Edfhr05k;IfF-Ah+L9$BCk*X@Y&-0PfbEpxMj~=j z0JKobz4%ev`qdp={WI-n33MC}sgU$p!3N?qW(0Z*N~EJQgQa8! zi!uYGr4p!4+E=uBQZ+u@&x0Vj^M4Tyt5X8}o&QM}JOA%QE_VK-aXP6h_?hA<0ZF)X zc%s0RJ|!UO?&2u{NjJ+=0zjqJDFJ9NpsKQd`C;})NMB_@a9SM~;4}2GAyc6YcE@o6 zX-2+tdojKbj|&*{F)&FaC?GEg2TGb$=&+kKsSvsup+e|ygbHP4Cj-eQz`!K873)lc zc8s*~00Wb1r?YO}CxNm{9FIu?IUbV)>Ti4!_-qU#Q2a+dj4vcb!ar(+#|0P!L`(FS zNj`|kK&TBwgb)Z%Cb1m>J0I2#9~s)Wm#D;Og>l~?hC{O;YlIFT=m~g_i1p(NMaPxC z0~Dps6NK;yDb|Ak;iN1oGK$Vg3Nh69xTZalMp&`~FX#{DF(E(h+twkl&kCR$3CpfeAO;YDb@}OD)%Uy;s66R<0wjlQ!?M}*2evruX zeEew41%WX!H%HVARSSQ|S#(m5X{SR0j^RLNJPdGDGarOJqPgm1-yqeMwAu}aZjR3Y z@i+!`iZn9P#9-#*kPfO=_*kATn}&HsMU$l&?FDTD<*2#fd>~riSq~73rcONN`Jp_D zmT%I}2hoduzBF~H3H+eZzvskZHgM8O1yhgA!}jxOD}}a>v|B+KAmyMH`jOK<;vdH0 zM26zv;?+R>{dhGIKg(<*CW<{Uj1fV2L0WN5o$Vujnv9%PnuyOTO~lvV_=qpR(P<{) zV{QX_2KqMAVFi&VTXklXa~edRjC@Z0?T(0Gg-RN-J)9>QzN9yh)y&Ti9Tk*_QUt}( zwG;q0iNK6diICA2Qi&h~t^WP*{nQ(99hCsQ4=EAwWFyfqOr+(O@97jEUYb3|d+A;tpU%agJL=8q55e;^AN-4_Fwl6oVaR8Yb3A!y^iE088^4U=Mty#1GU zlfWF9eWN4}a_0hKela+VDluz~Yp6|=OZ)Yr7U~+TH(C9a9Oi>{MVD0I5|z)S zhpPutpw+D0G7m$ooUZH)#1a>Oh-}u^$m3MYI0KGJb)>W}MSm^tuv>n>K*CsfYJqZ} zYl4t}J36Puh+}TTq$cxtHK@rgcr~fX4a_#E$w5?P+;0=`(}cR;MrIl`V5E%){50Vo zbtIejNtx^t$77N*j>jZrdOSWUdp3p&;RZ z-SK~68ZMG)Yd+~H32`(oz<^JFL;JtploUzd!i2yQL&$t`qNNt?HVBwlEy!D&2$+U= z3H=WD0^36Z<|a;K)4JqjAfg81B^>ZDHl=BkfoE7%hRZW?$uNa%ZjF?Q?&vg4KT>RZ9)7gUb{KRxq5(!^w*AO8 zWwtEdO_`0`bqx)0n1br;h}gs`O|gkpnqrgwCL%WJG*kKuHNZ>At-*6)+rnTt{`PPJlL} zK2?X#b>DDu36E=lHc^M`sHPx58|iSxYLl!O_#Rhi6L`E2)8QO?yj{AqLyPr(yhh1Z z)Z?w|_bt`|+KhVszhMQxw@8%P5&SR>7k30d?oX7q-snZxPbu*1C$5i;W4pHqS7V7LOX8#F`M_t7SGx6-d$i?U4@Gb3dwn)z(_ z&#s-8)ILUP^Sv+tY9Z-WJlrOvHh1CGpf-2n)ucAZnQa)hDyYbaulbwt=&>|9+ZU4L zNja?26p~n_DJ1D{d?87Gqtim7siq}x%1BR2aO66C3E;5O(g&W2yukd5oMH1T;;G#@ zc8s2up$v$)fFz9h%q`E1pF)U+n8s#rc`!@DInFusTlRlZI(cel5;T3*vL>;C&W2ZF z5?+v_0=||r(^D*n1bJQIq%Akt;dBNbY%rZg&J^M-3&>##1p>|mo>8sy}0=HzxpJOT9|Zq zA9SZqKX&g7>biRZ5!twbA^TQ#V0w}P+Pa7CRFJ45wBg55E^2N>JcM{Mbz=iItouXE z{<9hed^M0+c%wy(cMlq$JTyDkc?z^nv1LTv&IDMReD?~qM0GJhHtY#!>Lm|%E1)fyb+0fh$I1|nAQ!!qgu z5k$$3L!IJD0oXh#PsJE1O8v)Y3Z<}4Bcu$09HI}b8X096?JxpaZga9U+QtJ}dh~Nh z7j6z{kk^mh+Nnb8cu>y7$T=>1f$M~Fc z#~39hrjx)yx?nU8!U>GiuL2z8aVZJ>O`()ky^C4=SRGPBddjaQ3c3@#U1HKr_~8P2umbF>(V8$Tvg8`*#5g3Q45 zf*BDwaD)>w$zqsUKW=CS!9wmh!{>0Crn*zmNjWyuD7oXp=$Q-~#9GpS$1cg9rJhdD zsPy9eNQ^-BCWd>hbvNa@sqI1|q)fkjsg)TeS^UXMHjXfe$fb_l?sBI*a%~ZSfM{;; zbqfI&JQ`@vec~ zxNIPbxr>}m>)fenh;tOpc0nwhf~*ahz@20SYJzd{FzC6dTP=D170&O$8_un>P!Pvt z@tm*3I4%Vm?OgxWL_kR7={#SjjPx(RF@_O%akpW~RH;laE)^itiwn*e@#5+g^}V>y zhNKaAakm-PBH&y)*V%v5xvu-~JJ+}9zYcKJM~%leY=g+W#TLSFiwLb2Bb?)o?ANs9 z4zh9tk)v9b{U#hyVyLn+cr{en19&x6+1<=GRM|09G!}+>e$^){LWN1_Y+scX)t}mX zOg8?xzxnC+Buf?>fAk|i{hrVN^$-7=TejHv=RWxdZ}^=L{OK=$3D@MX^}l=Ky?^!k zH~-lO{~GUX{R{u?M<<}vbN=<~QV zS%4kplme!zxX`tH4;~4Zpg@8WV2@cfbkx%eQ!9Cy` z+YDnjW>WGEc>}Kzl%=QK=Nr$)G6d{=oBk8ZOm#GVfpNQ^I;89c1JTi zhqP%_ch5ZT(!L zW_^lSjOx&nKlZyMVQIs`F^@SSxEvYW*z&ot=EAhbAjJPIjN+~GzZ856vFsz{kkc@R zsvHOfR+&svMJ_WWA6sx}3+3G%%)=9ZaN?`V;laPM$glq^9+>qwA9%>@dvo~Z8Nd2F zc?7Kg{5h3wv1S=1U9u4&jh7F6(mE#o$*+~?24j1KW(TeSJFpZeTH(4|QBbs^a*s8% z?#PbFJ(jud|6#Y5dn_gYu{nT+FaKF8a@UHuGWOtjT+@}Izwuoe@*ABNLeN|7$Z`pP zxfm`(D;C5bJf-l29>_y$v+w3w(MoM8GmOfU@=*3Cbe_x&3_NyK045^_9$WCF;E~FX zGuw)e=>2{gk+MH2yNs^*tD7l?a;_W5li9ZtWL>}sh60)H|KMlDGz}A(JHxNCr)PqR zkZe= z$qlOfya+{P)ENc+JDozOoaYl>>&$`EGh=DBS(3T4PJtm}| z1;++uktbbCYKHzDM}`k@gfK7D!Mt%R4?_BRv5z{m)#V$^fiW71FqR;WAC>aR>lg#8 z@F{jI`2{&?vQcKljtR~{FwS2zpgDUQ^dzBfmtn`B{(oo~Fg$sqbx7DwES^ZF(qozN ziOH$#uHAd~?z`&h{ns2gc z->vvNhQH_I?>7A1j=v`$!dCwi|M?yM^HKiuUvOOx2zcf__SWE`rj1L0MZ{`e29Ti4 zMi9XQ6GY@OYe0K~5P*W9V1Ue{3aJP%jy#=HLDEkJ$u;cw8iEss{7DcQ_d$fPq~KC! zZ30yQA`yV2-2F)wgYM`mHiNC#C&ECEbKq1zW~uYZ zk^B)0w1iC2!RqQ=ZLJj6br+i#)r zih57-b?YvIwK|W2=kn;0>Z9oTFR88{wja$+Ckanfn%|q7kz6HJ9bcYi#TaB&^tf4P z+IEhsqxhg+%fYLSe~5y4Jjcj1zz+N@n5Sdn6X+Wo{C)hoI1~C~{KeVsA7P&{k5A~| zU*RwM{y6+f=zuz`5$CX96m)4IQuLVk_bkGx{v!KZ)&32h3ZxtyDz}=`mtm@Qehwd@ zHgReOwCJH+ReC^da7tW+g^nB(Kg(KwHdsJWg;7_)J4A{15o}$=4J2rv2U`P%*b{_j zJrcH2{gls^AaXbqIsTXxYFU$`HiZt3+LSst`iR)UX^6-jK70ihmp`{O+mv#7kR3`4 z?-L=T0~&}>VnECR>3qOQ(c_Ua_@Jws8;8CavyHn9xO-lsEFr-Ek3{Lg9J@{T)(m#0 z0U!kE2LJ-6Z3VC(NM=O!p`3O+$>J{qHR7Ea3^S&W42~rDL{fmwnEDX(4MqeXVBpCI zeZd!rGTJ2j=Y?_jI9iBANal!B$IvIbBgyo5WTGa{8`*5ejDog>_$diTShMmBr)#Xp1r^1)C3G7XUI+800i7q9)y z$3FSN&*7b2`-?yQ@K=B7OP~DEXYj7up4SFS*|ts#ArpN8evE250%+U3@jnou*aox+ zL@bKWZ_MHPXbLJ)c?}FmJlH&00B>A*;cIsw(GOBz1EMM4 zz9gbj0+obP;T6#Vv=yCIA|U9uFFn8CvBx5v))8&!G(unHxibpYam6vZZ6C)d(7TtQ z$4MZ+{O2VH5x?Znt%l=Xkb+?$KFi1cY2Yo1#`L%YM#8P-u=~1@J`nSyhJk&jyeDIf z*sU+Rp)^aQFR$&I!2vWE$sj@W!CR#;j6p+|V>3zAKYU8WHhC3`IJ zotkJyFz{}aBqtiNVsfG<(IzH38JTFDe4{2hjtnqGEEaiTbfR(G)-i$G37BIAON*@( zTw1l2BB`kw59su%8YOg1?Ks6UBp{%gAdS-q-2yPE#lo6l#lppUVc3vbj9_q>VKNh| z`r3;w`a^viorc~eZ}Q};&dEH5I^?0EW*t8-B0MeN6D~NlqAH^@g9T_Ssmca37)A|A zi!v`)h0EZ>`Aw;dn7V*;qyzY^>?JH4 z)ar~GwKHSDr0h>qtj(McAw3&NL_|BB6ZkVMAt@q6ZHa#J-grVDVQ-vD+pX5Pd{N<` zfJ*93_>t#~SyU5bzSP)3z32>6YrbvE?;B~$tKpbo(9sM*jTzL)H|EHJ$CR;xRBFE( zuG9-L3dCioQ(Ce37^R;KI)(X>VHf^DFWKDB2BWY^#>->L)C=QL^ts z>+-rI38Ht(H`oSb-f{T`ok8YR6KHI9iA!Du<(uBQRVR<7nemW3+K|v^)}37UO8Sc}l)mgdiB1 z)M%NEj>^%-jnV${=D?p&gC@WO@F@VN@jPZjnnYJ;G77kfk9dU$Ds{>+0>9j&hZD|- z9PAkLvE@o32l|I&pfQQGJm6qcNRTlggW9T?{SU#MCVVuIBpYvOb@F3?{;|w@1PxM~ zEs|^rn;y^}?xn@QJVGw}c+g4;t;{6l+TrLE+UDexxkw)ewV*tgB&|qc7^?nEH`1z4 z$b97c`Jn0$`OqY142Q;Wb?_r^*NQ2^29%UoMhYVhfygJC2JQWcaCqUN>8>(y?t^Sl z0yx%oTG+?!Bew^VfEfKGuY_js7Z8Vr*QbGbB0ynf~Y$u1QZ@;Fq5e6 z$Ib9~M(HuRM*^)UPBS$o&rOm|@~l4ltl}9kx(s?;VZEh-yCx7J?4Wh%C^*HAwOcM| zKKcs16pOHVX_QT(M{M0iM|pFjYqNijYh&PYq(YArdK9;BL`Q069*^rOl!HWK?>pI_4p! zrWq6BCQoBdF>&$JWUnrM$XRhW-YIZaHP^}Ng>Qk1idSstQgfYBbA2xcBtK#*PNFBU za?pwDy$naij1JUbAIwn^i4eCkI0H^G*CcrtTF=EU_TFy5n0c(Z5GkOxz3gGY>0GtHRA%N49 zKp2k#oU<9gaUCSU$=X5Xwgou7YFrio9JGGS7BHCy7Ql9>SKJ)}H)EV~x`{wDg0=ui zKLjw$&}oL^I|L4aeruo!>tQ-Zqno!fzbN1WimrhqT~^g!gCV3xr7#^4+oQm*(VVb6 zLtw|8+Y0j}!K}Xabq}VNrG+0QiGT*40&)oYP+$1fFv@Lg%gRimcf^kD+v!~iU*92L zS4HA}+hXODAI~ciSI|j z^xcmk6~PVm^}K#}d4167uEIYlt~yumcW!MicbljCN6*!lmuKa3 z+!Bi^!J5{(%ggn~vZ_hM1}8gxA*`4+(du>AX8l}Yi!~wEE{Nm8?yNdiEM{exH;Ykq z+iekd#H4JAiAVWQ9Iaa-s9r3lZO3K@gCzF-T+B*#R{Mkc>cFvV4h5RD912=6hY}OV z?bx}K-l~(TH{<}l3mkU3zuxFKd!2^IJ{&YhyV70l4!WzIrXI3`4uvx(7RT8kG!vOj zP6;RWH5eFs)N{tx2hCZGvF{CD6I-pXc>Sw8tFx{8`Q^@ulY`lQ{cLCT#4NwL5VNxW zx3jHIe{H#b9#h4hb6OqQe__QjW}Pb%!9sT6#5 zj$ch~_-YDY^-p)!MArODrT7z+Bs5$TK*Q{yJ8Sk<>HOLGPeL}nneHsb1_B&T4voPubH{f?ND z5Y_1(Rjb^OCxAAw$jUPzy6j?co$!|203sHXQyZbg-iUiK5`$E`v+VUduL15Q#7Zi; zzIqyTZFO8u$ZX3yiy6u`Pu6=Npk7ZWq+6YyM}m4j*6y^s6aDimjV|VLcDb{9dc5bI zSYNL9W?6Hh4~X*ynBkU3sx zIjc*Ii`7`73G5hn;TY52J`tF&ukWP53&f_*VAa&n)XQNJmWP>`FIwfI4qj4pQC9gGjG zOww1Cd9@-*vw(ut6P}R~T~(HHtOMeVlV0uW!r(4|Cd*2bQp(yyoUCruI~UgDSl5cv z*MRV=zF)8Ut|&j7@Yi$}?7Rp1{v5G#qn^Udx$gRMYgPhlvcAkEI6v8_w*aR;w8a|h z{qw07a!CB*XV$xedR$KXq=dLZw>t}HJ>l)^EH8T}>dUi@^MIs2p=`D3Wx#sYdfgLX z@_jr52YTyk06s9$v-Rap%kFf?JADK2mb=}xNmja2Up+4oqCbwY_v@HCF2Mvj8i*xY z?XI5SRH-#h;YkiKi3fId57_j28|%@b7_;K70Nr*+()w%lChF@B!sb!mVAfk%8=Rlw z6R@Fs*6R@jGwY~sZLQlw=M!jRFgQPnpZqR(=g$D6%wACn=e2H;ony4j`#(rK}hK)bz_`tq2X+(D?p5a+8%u z_6ZMDPs%3dkey~*e)c>FJF`gzL?IZyq5-Ts1>s79qbT~^@utmb!*$!yhU-*6*%A?4 zl$0z|zJLx@*dfJuZwdzO&;NoimfVVxNwXwGfwtEx2P<_fkP4oUqKC~<-Ucy2E@H6! z;sq33Fm0GIEeG8cBZ2R;qk9x(53yyoJ`lH~)`MoPPk=zhNX%el@cVBti3wdFizQi% zl`M+makb1FOFq?U9p@6sX=Mv93|g4%g5rftYa->p_a{kKL*%$A#rI(JNy$x6)i-0r zH*v&?t-5S5x2d=Uv5ewhyR84jVN2eJnqGvO`m9OG+x}GlF3M!MFn)`7V(xn&U}p^u zt>)gxj>{bc=#ZqJX#B;&>VzLhM?B{9dX)Z_01-E?kYqS}Bq(dnh!0)@;96gmBanOo zNw;rPhd)06vjdt@%FiSj?f1b^`yBDN#;oV)>;PzIH&;Acrt% z3cOJA38UEVAT3fffpMTd;)Es;6i26|I2wYBcqzjnT4eCE z?fOb*`TQ(dXVAvgLzyd`{z`q&Jb9JR2npGc7<92cowc(C$th=nCOs)kF|9O|mL+m3 z=@?Q1SZ!9zF94H4y?GKt^%EM909(8KHw)-r7lT1>IYQ#i2Lo&wBTqtgln5-mQnTzKT@`s6uoInqG#mid9%WAsYO;ZAc&eQIm_rFS~ z`c1&nY^MbzQLr@p{bbOa3Ei71^oFAyE}8YwRvMkgCE9Ad6T5x;hWe;~RAzD5mp(+vdXs9^dQ1XT&79vI*I#!ox>&KFbi1;Lspcp;XLwnKKXg-i`v%loPm-hb zmjLY)Wf_N14_&h5&91N3&jOeU=oc(>aMw`n5T+)@=_Kl%V!etSXDrxbK#hqaoMFh` z8iGP=OD3eoenDNvr^c|m_|--2d3$_&Ua(Yavl1<#)0|#ki)a0%h{BXud=cmFN!lW! zE4LA%{ZPc5mm%AsFwsQlP>9ps8 z2`ppb#WcFaqDImTlp|=KEf2P2=1Va=NSHx~?=w4m2*9`wlXty8m^}&my=3L$#-UE# z$4>2IOB>7HgeuRORX$fr%Mvo7RZ|;MeLt+nrJ**GhjC>I#gRmOu$OIa$haX~6xhZM zi~h|)zoU4ti~Uder~yschrm#f43SIR;)ra78^j|$W|UgY5MiTSgek)~FG=_o zgjtdeSc{E%!7VVMNr3WkF$8XLD}~k4N}$n`v?Hz`?(eKFrbu0UuUIUzb5W`#jjfRp zY|U@Bpjww?J7epukZO%lzlKmjVQPGcYJmRIu_A>m$C@DB0^?8$c*dt&P84IMVcVoz zfpn0e$vZ-~V9y_}Y`6#fW{7S9ZiXq;F02pkSn^>gV_^Y(QY=6nl`@$L1~bWOC5;9Z z42I)FW3!AcE zN}DmrDKk@w=>%4|vLPCb)pyzE~`PG{h(XA-&q;AEp zCVq`rbm;3l$L^uy6MhbDxGh8=h-=K=MRMZ@#9|U3!(FlFd%maG18?OSj+D|v!>FsH9Gqc389-Oc3=IC0-QYJ3+z2#|;y=3KETg*af3kqB!5Dep${Qqo#b z^r&Y{>T@>CQfqMaS{6Q&-Sw>(9SGKNmjHNhE_U!O3omH06vFyYue5??N_zUJ)(Uhrx!yA+QR>;3iTYlJ(v)Dw)t$DdqV`!X*4j zgKo3C9HeB|q06mFqfU^9*_E?QGXk{FM7!fHx8Oj8LqEv@dOf^AR)S9wdZhn01^u$S zLKnXuGXNED3J`OmYbH9Z61uuBt6i#&wOghK#e?*{;`Dhq=zK$uG)tq@>`MQteeF&U zvafWS`6EU0{s#1a4=z$>^vw^@A6+jDWO4-@>uR=36&OJ~eQy?3ufgNI;#k*+AAlI5 zFi|^_4$87d-_=Ksb)C#GI`3yZhZ+0Z>jTo%O+F->)AKt)Xw68Yh?;P5gC>55P$)?U z9Vp0+ocJ*s@N}V@BmuPjN|f$zJr{*kfmr}1d>6-^XbB}zJeO_y1tG5sO5xFhECGd$ z5J1fYkW3kT)7m7y`DK+?O59=$(7I7dWU?wf96?`}-ynxJuA4&S(yAwB#px*&5dADo zh21_@d*3u7Y-Ih7p5f|BA;~R?_t4xa>|1`b(Y8*<@SZ;g6jNo zb4Iy6r1^*#io!Nb#6)BK%Nt74Vh zLp}9%^o3Cl^v#76VZkP~e?wu7?4k^bFiXZ1Oq^BOtUL%@AKX_gh~o)OzLLsk1?M;V z-%qHn71KdC`aVUOv=9ulmw>%tis`7^Wj4Rhf@|)o;c_Y{Lp&e-L_~zbzylH#!{^8)L3EgNtAynFR$?GkMAm&P_ zLZGRX2aS=4fFo*(QbkHLC%MOlo&+xG*lJV`yP?*I#6|;Br;2 z-Y`DBEO(%&XwPsWu2V#A3!*6Q#IjtEWjXzyx#sxB`Y1lSb}J$(3EWF`N!S!-wDAQ& zVOxD7zSWO?Iyo4u9i^+fAHM+-;ni8A`;tU3ahPc^ZA+T2gCAj>*-)1jCqV^=Yji9r zQQovIuZW4$uq@a<4hmn2NHEGUp(GlS^!;6OWWi{BdFQ$07D~vBK~(M^W;lX_&?50o zF>Ne|Kzg?0ojVF521iE=?in~Jz~@e=D#M2qA^@3k+)k)!Y$sH8CsY+dJ}c1h;Vha#^ukW4>Q1QY z?C|b@OGg0jgsO61%TT=SPN*uk%OB|qQtgDQs`y>)gYjBBp{nqsX}`u!sA@E< zkbC8{{=O5c8i^~uWX#Y`sOnCrY8d_*4f)y$RfXlazZ0su6RL`R={upS@TABv6D@?u z*rACw2N(99z@FBh~xcy&e^@5HMI(Sd%PD-XEZ ziB}Kf(GjVM9fUja>ixkUYbRb^hRyH9tBXbWMuK4Zop|+UD_$LE>{^KpG3HDhjxJ}~ zrQ+4cwvJcFF7x3Ub-X$@gl$4)ugYpv_w`_?;>7Bx!h)uSa$?bgD zp1kohjKz$q#~Gq}%LUk@LkZRot&hd@QBpFrKIoGlTAz*SEtoIpBHcfM*^czH`7c_ZR!eD4kb#6=+Ac*g4?YKeIAC^m*ri=bZzd{qtdYZ{*Ga z&;DKqniGwHcD`;rb$RE2=bZzdd398XLOTaM^W1;2bHFpsl-)Vt8COY(!&@EPx^uuY z%vix`?_$IGuFQ*`@49oqGp`WXIpCRx%)=KjbjbY90ngC=j02N*4tR!OqK|&w{($Fa z>=0cX^1O41?#>~)n}t^F9HPrxY$6Ba?i`}K@r2i%Lv(pDwv27UZpfWObae>U&LO%% zFq*ssR{WbkME3wJYEzp9B~A^85VGKw!Grn`U0eaRp)P%hE>1}vu2Bai;%=xd>%B5T ziMVxPWcN}$2lKMYy%a72=)(Il^&D)x^o)cfM>=DBl)gfE z`+F&FxcvJh5ULs8@nBwWfsFW}%dZFf(KoLL8Sxv3_xACYN*ulwbF5k#!Je)4ChkHI z<9Os9>$oMpao(Od*BOAM<2p&+8JWl($f*$U^jOUIl9LZ&_68iQk zKZF$Ju-4XFUn!6Ex4!FDyuV{f=jNA!dY5qjJFW)r9noDnqTg>V<@2R_-J5R}nyqGY z&TB5T=PSi}E?+3s7v|>*xppqM&}`Pl=U?$|VKtV%``hN`WE=g}`da^FcMvp|&xi2W z&*#56xaGV*kF&w^3%F*0W#wHaOC4UHImnk!mfD4dg_bwptk=u=e6d**E8DbD;hf;I zh<<^6DB%u(=IMUExYEUSqFvl{wj@*XIZ#D?0CPTvDGHmbFXS4Hdb>0?@0IeEQoUxM zeA3P3&efNfFWJ&Uz0_(H=9^8-V!kxjsyW`1ZfSU6^SH2nXh2?TK3^{7>TRr7Yp%FZ zi@o|uH)9NFlNL((xjZVKFSc4S;Gt zA>{K2`JuLo#n!??d7)C7#{w1$bG6hJXbX3AEcIc39cpX7SoZ40dZE#nZ?sB<`C9tK z(;in*iX1}?<;%HxWg+ji+FowKD>Z6kXP$OL`2wg`D}=ED%73+5xQO>*aDQ*H~z`bH!3^{Or?iC%>Rfz2V@N>Vu(ny?nEg18JUbm5YT+ zsac!oJneSNygYnkQy|2JN^@ahp;@lC%X8)0|!Z8yYn1!S!4wd%RKxw+DO zsWx>5a9mx-zHyMbX1fkrS$LJk5&Ggmu!`E1v~ zjl2?2@gi%%Ub6&ofE6*1e8jafZ-+kF_8l&4(V97OdjZy=bZRbk$ zVi}UrbFRdAq`-2vQ{SAP)SInBd%jRC&F2@2^K)L!?O*nIww%L~6vvjfY;+D>VJ=^2 zv`h7J17KO39z6X<%jy=lty^8l=gJibgw2IY0g_XzHgox;vjRnZi5GJgr5!+Okn(8= zJo3$avs@@cJ*ebc^W|pkhRavAf;LL(lFEFJ)WLs_=#G_zLa|*cwdUIM#Y&}Kt{r-c z&A?U=G$WrQ&D60t?fiUWq3IQJ3y_6w{NBr6r(iS%UJ5Gz8Jeqw`9h&lX*Y_kQXRr% zp?1@EU3TpTDoKfasOn<7S!v};<-*+DLZw_N)t=jYDph0XRO_MTlk&HQ8p-7vn0qX9 ztFSO%uas+tTTiKxLeR)&T1+`t$QRq?Qh8yaR3XQ@`SQu>RtO6H{A{N`%=Rk_xk6>3 zIp3&wSd&)1_Dz>Bs%_PhM3nY2?o=6?JjjC!ZN1=`^)EJKrr%kG;^$boZn zZO@x)6>CQ>Utrp5r6By)2OH)0=3INB)XKG+IS=pwIppZ&3%FY~m7fb1YGZ?qLa{zq z06Wh?xGGdCwcHgL8!desn<~wNmA4vml?DcvYv*hE?d06clppehi_KNqN;y|5v|5#N zk-Ftvt~Ph>vI)jkb2$eCqiK*(=BpuiDZ^?8^&V}41vVS`+Wa0V&MMQVFVz+nw&{%) zh1Ui>2*1rbtVG2E&=&R^2$Qwq?c031u(-amR;3DD^rdIW`R&qtqgg1Ib8}5Ba_P=( zD#+(wEN!$^DSOk3dUC1G`p}g|qtPxdl)X}&$k&>$mDSL2Q828!{?IBZDGY<3F1%oy zhSVTKF&Z}EX5DM&^9vp{8<_suwOc1;O|)zEmr&oajF#v5^86QFvhoc&0LGBdH(<;u zRA3N;L|H7>ZrkNo8np9*329HpfMm0`PpVf^JGOj0-EP6KPO4sAymTib_VMz`ve#}E zO7ruDx%pv9YzeN`F6MXyQ zOZJ(?0@CxX92UF)_0~%#)stV%x3LSzk?$}JFEK9SWt`zP$)WE`;*lSI`IVK;>@le2o_87sDk|$J?Rxx!j?8U;Z?^J@3eo z4xJ@Ocz^YvuijYh$dTZSJMKPIz|Gj4>iR;vSZU{|H$t13E7o4YsRpjBuQa^g-Rw9n zvp@Yx`GCp)4n7RL<>l|>Y{=2QY9DX*T^cq9e%CIUY)w*DJ=0GpS8isw0yyeob9N`K5h~{H6c^`C)aVG{JGWBmq8)v zU;;)p>aZh1fo~OZuoHN-<%PZbw%1*ZG_$zh7F1qltaXmFCM0IBif@u9B;Dx3g|;R!1?c zkCu#OR9b4cD|3xpu?#sCX6VW`173UOHUp0M2+OaKC(UZZiR7r-0R-wb-^k79^94*} zxmAbJ^voY~DYx`J78M0xcMT*)q|8~bH#l)iy=CSu3 z+l|WP7{P2wYn#&bgXyY!4Hyl}3v=a?w@@m#YyFpAZoL~lwdIow?M5Njs#DVN=Ah5j z1`kU*o6Ac&t0*K$_4+dA_T}=IvwQcU7apnwGo#7rRz&&k9!v*N@?N=m4o~WHPAW01 z2)ZjXm)2!2X0F_B!h{Z1JqOcVVXjhJ|F)+z!UBmR(WC+$z6DAFX}2*~%Gb_b_DV!% zET0Ea^ZnXf;r3j!40mNA4^4Oh{@8QdnGhrR5UIB)c|R&Fv|+G84~xZou>gyH?VguE zrGZy`euO#!fbCMV0S|b)UhsUL9uk%J>RN&!-mHe<}11RIwEoE zxrK7RU3>kNXd%!Xz!4gA3(W$;YwGPhJmZaG?T5s6EFrp_Ta7^(iOFvAEpXdr1y+kj z5oX7Su0-Wj6wz~dp~?WyO1*?IGDyC)9~R&Dv}ZrC%au>I=H^@N`U0XN^005u7i&Kv zF5j%Z)yxbTeqx7UusAS7H>t1rD<`zfjCUX_+e_C}+M^uf0)Rz8_}G zQAEscwA(bT@(Rs`7%Q- z!6SN0jrGp*ptIVaU-4E-_-n462bdzVrBy7>7aN5-9w4E=LzK4uu8i*(GlLIHg@t;) z*{&4IxpoDiRke3sszG!S-xcO_??bQc} gaOm5&Ou^K}P>R_T%iTtOxqnNl-#M{53#-ik54JP>d;kCd diff --git a/wit/uqbar.wit b/wit/uqbar.wit index 821848db..1ed0a1f1 100644 --- a/wit/uqbar.wit +++ b/wit/uqbar.wit @@ -1,16 +1,16 @@ -package uqbar:process@0.3.0 +package uqbar:process@0.4.0; interface standard { // JSON is passed over WASM boundary as a string. - type json = string + type json = string; - type node-id = string + type node-id = string; // context, like ipc, is a protocol-defined serialized byte array. // it is used when building a Request to save information // that will not be part of a Response, in order to more // easily handle ("contextualize") that Response. - type context = list + type context = list; record process-id { process-name: string, @@ -102,20 +102,20 @@ interface standard { } // system utils: - print-to-terminal: func(verbosity: u8, message: string) + print-to-terminal: func(verbosity: u8, message: string); // **more will be added here with regard to blockchains** - get-eth-block: func() -> u64 + get-eth-block: func() -> u64; // process management: - set-on-panic: func(on-panic: on-panic) + set-on-panic: func(on-panic: on-panic); - get-state: func() -> option> + get-state: func() -> option>; - set-state: func(bytes: list) + set-state: func(bytes: list); - clear-state: func() + clear-state: func(); spawn: func( name: option, @@ -123,64 +123,66 @@ interface standard { on-panic: on-panic, capabilities: capabilities, public: bool - ) -> result + ) -> result; // capabilities management // gives us all our signed capabilities so we can send them to others - get-capabilities: func() -> list + get-capabilities: func() -> list; // gets a single specific capability - get-capability: func(issuer: address, params: json) -> option + get-capability: func(issuer: address, params: json) -> option; // attaches a specific signed capability to our next message - attach-capability: func(capability: signed-capability) + attach-capability: func(capability: signed-capability); // saves capabilities to our store, so we can use them - save-capabilities: func(capabilities: list) + save-capabilities: func(capabilities: list); // check to see if the sender of a prompting message has a given capability, issued by us // if the prompting message has a remote source, they must have attached it. - has-capability: func(params: json) -> bool + has-capability: func(params: json) -> bool; // generates a new capability with our process as the issuer and gives it to the target, // which must be a locally-running process. - create-capability: func(to: process-id, params: json) + create-capability: func(to: process-id, params: json); // take a signed capability and save it to a given locally-running process - share-capability: func(to: process-id, capability: signed-capability) + share-capability: func(to: process-id, capability: signed-capability); // message I/O: // ingest next message when it arrives along with its source. // almost all long-running processes will call this in a loop - receive: func() -> result, tuple>> + receive: func() -> result, tuple>>; // gets payload, if any, of the message we just received - get-payload: func() -> option + get-payload: func() -> option; // send message(s) to target(s) send-request: - func(target: address, request: request, context: option, payload: option) + func(target: address, request: request, context: option, payload: option); + send-requests: - func(requests: list, option>>) + func(requests: list, option>>); + send-response: - func(response: response, payload: option) + func(response: response, payload: option); // send a single request, then block (internally) until its response // the type is Message but will always contain Response send-and-await-response: func(target: address, request: request, payload: option) -> - result, send-error> + result, send-error>; } world lib { - import standard + import standard; } world process { - include lib + include lib; - export init: func(our: string) + export init: func(our: string); } From d28242d93fc63052ce17b06dfe6ce965a8087670 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:12:02 +0000 Subject: [PATCH 32/40] Format Rust code using rustfmt --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 6e341bde..e786f72b 100644 --- a/build.rs +++ b/build.rs @@ -153,7 +153,7 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if !["homepage", "http_proxy", "qns_indexer", "terminal"].contains(&package_name) { + if !["homepage", "http_proxy", "qns_indexer", "terminal"].contains(&package_name) { continue; } From 6e9c74dd0ba324ddc90497360e6b6c81c6ef432b Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Mon, 6 Nov 2023 15:35:53 -0800 Subject: [PATCH 33/40] update key_value & sqlite to use new wit & process_lib --- modules/key_value/key_value/Cargo.lock | 236 +++++++---------- modules/key_value/key_value/Cargo.toml | 4 +- .../key_value/key_value/src/kernel_types.rs | 1 - modules/key_value/key_value/src/lib.rs | 139 +++++----- .../key_value/key_value/src/process_lib.rs | 1 - modules/key_value/key_value_worker/Cargo.lock | 191 +++----------- modules/key_value/key_value_worker/Cargo.toml | 4 +- .../key_value_worker/src/kernel_types.rs | 1 - modules/key_value/key_value_worker/src/lib.rs | 119 ++++----- .../key_value_worker/src/process_lib.rs | 1 - modules/sqlite/sqlite/Cargo.lock | 236 +++++++---------- modules/sqlite/sqlite/Cargo.toml | 4 +- modules/sqlite/sqlite/src/lib.rs | 131 ++++------ modules/sqlite/sqlite_worker/Cargo.lock | 238 +++++++----------- modules/sqlite/sqlite_worker/Cargo.toml | 4 +- modules/sqlite/sqlite_worker/src/lib.rs | 106 ++++---- 16 files changed, 513 insertions(+), 903 deletions(-) delete mode 120000 modules/key_value/key_value/src/kernel_types.rs delete mode 120000 modules/key_value/key_value/src/process_lib.rs delete mode 120000 modules/key_value/key_value_worker/src/kernel_types.rs delete mode 120000 modules/key_value/key_value_worker/src/process_lib.rs diff --git a/modules/key_value/key_value/Cargo.lock b/modules/key_value/key_value/Cargo.lock index 20dc8e5c..12506229 100644 --- a/modules/key_value/key_value/Cargo.lock +++ b/modules/key_value/key_value/Cargo.lock @@ -17,12 +17,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" @@ -30,28 +24,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "equivalent" @@ -60,12 +36,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "form_urlencoded" -version = "1.2.0" +name = "getrandom" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "percent-encoding", + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -89,16 +67,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" version = "2.0.0" @@ -122,10 +90,10 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "serde", "serde_json", "thiserror", + "uqbar_process_lib", "wit-bindgen", ] @@ -135,6 +103,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + [[package]] name = "log" version = "0.4.20" @@ -142,16 +116,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] -name = "memchr" -version = "2.6.3" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" @@ -162,17 +130,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "quote" version = "1.0.33" @@ -182,6 +139,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "ryu" version = "1.0.15" @@ -271,51 +258,12 @@ dependencies = [ "syn", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -329,40 +277,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand", + "serde", + "wit-bindgen", ] [[package]] -name = "version_check" -version = "0.9.4" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -371,9 +320,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -381,19 +330,17 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ - "bitflags 2.4.0", + "bitflags", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -402,54 +349,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -459,16 +394,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/key_value/key_value/Cargo.toml b/modules/key_value/key_value/Cargo.toml index 0c6d8e49..3a422184 100644 --- a/modules/key_value/key_value/Cargo.toml +++ b/modules/key_value/key_value/Cargo.toml @@ -13,11 +13,11 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +uqbar_process_lib = { path = "../../../process_lib" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } [lib] crate-type = ["cdylib"] diff --git a/modules/key_value/key_value/src/kernel_types.rs b/modules/key_value/key_value/src/kernel_types.rs deleted file mode 120000 index 047e48bc..00000000 --- a/modules/key_value/key_value/src/kernel_types.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/kernel_types.rs \ No newline at end of file diff --git a/modules/key_value/key_value/src/lib.rs b/modules/key_value/key_value/src/lib.rs index 8f7f8afc..70c1dc19 100644 --- a/modules/key_value/key_value/src/lib.rs +++ b/modules/key_value/key_value/src/lib.rs @@ -1,22 +1,21 @@ -cargo_component_bindings::generate!(); - use std::collections::HashMap; -use serde::{Deserialize, Serialize}; +// use serde::{Deserialize, Serialize}; -use bindings::component::uq_process::types::*; -use bindings::{ - create_capability, get_capability, has_capability, print_to_terminal, receive, send_request, - send_response, spawn, Guest, -}; +use uqbar_process_lib::{Address, ProcessId, Request, Response}; +use uqbar_process_lib::kernel_types as kt; +use uqbar_process_lib::uqbar::process::standard as wit; + +wit_bindgen::generate!({ + path: "../../../wit", + world: "process", + exports: { + world: Component, + }, +}); -mod kernel_types; -use kernel_types as kt; mod key_value_types; use key_value_types as kv; -mod process_lib; - -struct Component; const PREFIX: &str = "key_value-"; @@ -41,30 +40,24 @@ fn make_db_cap(kind: &str, db: &str) -> String { fn forward_if_have_cap( our: &Address, operation_type: &str, - // operation_type: OperationType, db: &str, ipc: Vec, db_to_process: &mut DbToProcess, ) -> anyhow::Result<()> { - if has_capability(&make_db_cap(operation_type, db)) { + if wit::has_capability(&make_db_cap(operation_type, db)) { // forward let Some(process_id) = db_to_process.get(db) else { return Err(kv::KeyValueError::DbDoesNotExist.into()); }; - send_request( - &Address { + Request::new() + .target(wit::Address { node: our.node.clone(), process: process_id.clone(), - }, - &Request { - inherit: true, - expects_response: None, - ipc, - metadata: None, - }, - None, - None, - ); + })? + // .target(Address::new(our.node.clone(), process_id.clone()))? + .inherit(true) + .ipc_bytes(ipc) + .send()?; return Ok(()); } else { // reject @@ -73,19 +66,18 @@ fn forward_if_have_cap( } fn handle_message(our: &Address, db_to_process: &mut DbToProcess) -> anyhow::Result<()> { - let (source, message) = receive().unwrap(); - // let (source, message) = receive()?; + let (source, message) = wit::receive().unwrap(); if our.node != source.node { return Err(kv::KeyValueError::RejectForeign.into()); } match message { - Message::Response(_) => { + wit::Message::Response(_) => { return Err(kv::KeyValueError::UnexpectedResponse.into()); } - Message::Request(Request { ipc, .. }) => { - match process_lib::parse_message_ipc(&ipc)? { + wit::Message::Request(wit::Request { ipc, .. }) => { + match serde_json::from_slice(&ipc)? { kv::KeyValueMessage::New { ref db } => { // TODO: make atomic // (1): create vfs drive @@ -100,76 +92,59 @@ fn handle_message(our: &Address, db_to_process: &mut DbToProcess) -> anyhow::Res // (1) let vfs_address = Address { node: our.node.clone(), - process: kt::ProcessId::new("vfs", "sys", "uqbar").en_wit(), + process: ProcessId::new("vfs", "sys", "uqbar"), }; let vfs_drive = format!("{}{}", PREFIX, db); - let _ = process_lib::send_and_await_response( - &vfs_address, - false, - serde_json::to_vec(&kt::VfsRequest { + let _ = Request::new() + .target(vfs_address.clone())? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: vfs_drive.clone(), action: kt::VfsAction::New, - }) - .unwrap(), - None, - None, - 15, - ) - .unwrap(); + })?) + .expects_response(15) + .send_and_await_response()??; // (2) - let vfs_read = get_capability(&vfs_address, &make_vfs_cap("read", &vfs_drive)) + let vfs_read = wit::get_capability(&vfs_address, &make_vfs_cap("read", &vfs_drive)) .ok_or(anyhow::anyhow!( "New failed: no vfs 'read' capability found" ))?; let vfs_write = - get_capability(&vfs_address, &make_vfs_cap("write", &vfs_drive)).ok_or( + wit::get_capability(&vfs_address, &make_vfs_cap("write", &vfs_drive)).ok_or( anyhow::anyhow!("New failed: no vfs 'write' capability found"), )?; - let spawned_process_id = match spawn( + let spawned_process_id = match wit::spawn( None, "/key_value_worker.wasm", - &OnPanic::None, // TODO: notify us - &Capabilities::Some(vec![vfs_read, vfs_write]), + &wit::OnPanic::None, // TODO: notify us + &wit::Capabilities::Some(vec![vfs_read, vfs_write]), false, // not public ) { Ok(spawned_process_id) => spawned_process_id, Err(e) => { - print_to_terminal(0, &format!("couldn't spawn: {}", e)); + wit::print_to_terminal(0, &format!("couldn't spawn: {}", e)); panic!("couldn't spawn"); // TODO } }; // grant caps - create_capability(&source.process, &make_db_cap("read", db)); - create_capability(&source.process, &make_db_cap("write", db)); + wit::create_capability(&source.process, &make_db_cap("read", db)); + wit::create_capability(&source.process, &make_db_cap("write", db)); // initialize worker - send_request( - &Address { + Request::new() + .target(wit::Address { node: our.node.clone(), process: spawned_process_id.clone(), - }, - &Request { - inherit: false, - expects_response: None, - ipc: ipc.clone(), - metadata: None, - }, - None, - None, - ); + })? + .ipc_bytes(ipc.clone()) + .send()?; // (4) db_to_process.insert(db.into(), spawned_process_id); // TODO: persistence? - send_response( - &Response { - inherit: false, - ipc, - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(ipc) + .send()?; } kv::KeyValueMessage::Write { ref db, .. } => { forward_if_have_cap(our, "write", db, ipc, db_to_process)?; @@ -187,26 +162,24 @@ fn handle_message(our: &Address, db_to_process: &mut DbToProcess) -> anyhow::Res } } +struct Component; impl Guest for Component { - fn init(our: Address) { - print_to_terminal(0, "key_value: begin"); + fn init(our: String) { + wit::print_to_terminal(0, "key_value: begin"); + let our = Address::from_str(&our).unwrap(); let mut db_to_process: DbToProcess = HashMap::new(); loop { match handle_message(&our, &mut db_to_process) { Ok(()) => {} Err(e) => { - print_to_terminal(0, format!("key_value: error: {:?}", e,).as_str()); + wit::print_to_terminal(0, format!("key_value: error: {:?}", e,).as_str()); if let Some(e) = e.downcast_ref::() { - send_response( - &Response { - inherit: false, - ipc: serde_json::to_vec(&e).unwrap(), - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(serde_json::to_vec(&e).unwrap()) + .send() + .unwrap(); } } }; diff --git a/modules/key_value/key_value/src/process_lib.rs b/modules/key_value/key_value/src/process_lib.rs deleted file mode 120000 index 9b9ec3f4..00000000 --- a/modules/key_value/key_value/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/process_lib.rs \ No newline at end of file diff --git a/modules/key_value/key_value_worker/Cargo.lock b/modules/key_value/key_value_worker/Cargo.lock index 7b745f19..27064d92 100644 --- a/modules/key_value/key_value_worker/Cargo.lock +++ b/modules/key_value/key_value_worker/Cargo.lock @@ -47,42 +47,12 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -[[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] - [[package]] name = "cc" version = "1.0.83" @@ -104,15 +74,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - [[package]] name = "getrandom" version = "0.2.10" @@ -151,16 +112,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" version = "2.0.0" @@ -184,11 +135,11 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "redb", "serde", "serde_json", "thiserror", + "uqbar_process_lib", "wit-bindgen", ] @@ -240,12 +191,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - [[package]] name = "pin-project-lite" version = "0.2.13" @@ -267,17 +212,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "pyo3-build-config" version = "0.19.2" @@ -442,21 +376,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.32.0" @@ -479,36 +398,12 @@ dependencies = [ "syn", ] -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -522,22 +417,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand", + "serde", + "wit-bindgen", ] -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -546,22 +435,23 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -570,9 +460,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -580,19 +470,17 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ - "bitflags 2.4.0", + "bitflags", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -601,54 +489,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -658,16 +534,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/key_value/key_value_worker/Cargo.toml b/modules/key_value/key_value_worker/Cargo.toml index 2b6d9f17..91ed4be5 100644 --- a/modules/key_value/key_value_worker/Cargo.toml +++ b/modules/key_value/key_value_worker/Cargo.toml @@ -13,12 +13,12 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } redb = { git = "https://github.com/uqbar-dao/redb", rev = "8e192d9" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +uqbar_process_lib = { path = "../../../process_lib" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } [lib] crate-type = ["cdylib"] diff --git a/modules/key_value/key_value_worker/src/kernel_types.rs b/modules/key_value/key_value_worker/src/kernel_types.rs deleted file mode 120000 index 047e48bc..00000000 --- a/modules/key_value/key_value_worker/src/kernel_types.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/kernel_types.rs \ No newline at end of file diff --git a/modules/key_value/key_value_worker/src/lib.rs b/modules/key_value/key_value_worker/src/lib.rs index f70f44c9..572e074d 100644 --- a/modules/key_value/key_value_worker/src/lib.rs +++ b/modules/key_value/key_value_worker/src/lib.rs @@ -1,28 +1,29 @@ -cargo_component_bindings::generate!(); - use std::collections::HashMap; use redb::ReadableTable; use serde::{Deserialize, Serialize}; -use bindings::component::uq_process::types::*; -use bindings::{get_payload, Guest, print_to_terminal, receive, send_and_await_response, send_response}; +use uqbar_process_lib::{Address, ProcessId, Response}; +use uqbar_process_lib::uqbar::process::standard as wit; + +wit_bindgen::generate!({ + path: "../../../wit", + world: "process", + exports: { + world: Component, + }, +}); -mod kernel_types; -use kernel_types as kt; mod key_value_types; use key_value_types as kv; -mod process_lib; - -struct Component; const PREFIX: &str = "key_value-"; const TABLE: redb::TableDefinition<&[u8], &[u8]> = redb::TableDefinition::new("process"); fn get_payload_wrapped() -> Option<(Option, Vec)> { - match get_payload() { + match wit::get_payload() { None => None, - Some(Payload { mime, bytes }) => Some((mime, bytes)), + Some(wit::Payload { mime, bytes }) => Some((mime, bytes)), } } @@ -38,21 +39,21 @@ fn send_and_await_response_wrapped( ) -> (Vec, Option) { let payload = match payload { None => None, - Some((mime, bytes)) => Some(Payload { mime, bytes }), + Some((mime, bytes)) => Some(wit::Payload { mime, bytes }), }; let ( _, - Message::Response((Response { ipc, metadata, .. }, _)), - ) = send_and_await_response( - &Address { + wit::Message::Response((wit::Response { ipc, metadata, .. }, _)), + ) = wit::send_and_await_response( + &wit::Address { node: target_node, - process: kt::ProcessId::new( + process: ProcessId::new( &target_process, &target_package, &target_publisher, - ).en_wit(), + ), }, - &Request { + &wit::Request { inherit: false, expects_response: Some(timeout), ipc: request_ipc, @@ -69,20 +70,19 @@ fn send_and_await_response_wrapped( } fn handle_message ( - our: &Address, + our: &wit::Address, db_handle: &mut Option, ) -> anyhow::Result<()> { - let (source, message) = receive().unwrap(); - // let (source, message) = receive()?; + let (source, message) = wit::receive().unwrap(); if our.node != source.node { return Err(kv::KeyValueError::RejectForeign.into()); } match message { - Message::Response(_) => { unimplemented!() }, - Message::Request(Request { inherit: _ , expects_response: _, ipc, metadata: _ }) => { - match process_lib::parse_message_ipc(&ipc)? { + wit::Message::Response(_) => { unimplemented!() }, + wit::Message::Request(wit::Request { ipc, .. }) => { + match serde_json::from_slice(&ipc)? { kv::KeyValueMessage::New { db } => { let vfs_drive = format!("{}{}", PREFIX, db); match db_handle { @@ -90,7 +90,7 @@ fn handle_message ( return Err(kv::KeyValueError::DbAlreadyExists.into()); }, None => { - print_to_terminal(0, "key_value_worker: Create"); + wit::print_to_terminal(1, "key_value_worker: Create"); *db_handle = Some(redb::Database::create( format!("/{}.redb", db), our.node.clone(), @@ -98,7 +98,7 @@ fn handle_message ( get_payload_wrapped, send_and_await_response_wrapped, )?); - print_to_terminal(0, "key_value_worker: Create done"); + wit::print_to_terminal(1, "key_value_worker: Create done"); }, } }, @@ -107,7 +107,8 @@ fn handle_message ( return Err(kv::KeyValueError::DbDoesNotExist.into()); }; - let Payload { mime: _, ref bytes } = get_payload().ok_or(anyhow::anyhow!("couldnt get bytes for Write"))?; + let wit::Payload { ref bytes, .. } = wit::get_payload() + .ok_or(anyhow::anyhow!("couldnt get bytes for Write"))?; let write_txn = db_handle.begin_write()?; { @@ -116,14 +117,9 @@ fn handle_message ( } write_txn.commit()?; - send_response( - &Response { - inherit: false, - ipc, - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(ipc) + .send()?; }, kv::KeyValueMessage::Read { ref key, .. } => { let Some(db_handle) = db_handle else { @@ -136,36 +132,31 @@ fn handle_message ( match table.get(&key[..])? { None => { - send_response( - &Response { - inherit: false, - ipc, - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(ipc) + .send()?; }, Some(v) => { let bytes = v.value().to_vec(); - print_to_terminal( + wit::print_to_terminal( 1, &format!( "key_value_worker: key, val: {:?}, {}", key, - if bytes.len() < 100 { format!("{:?}", bytes) } else { "".into() }, + if bytes.len() < 100 { + format!("{:?}", bytes) + } else { + "".into() + }, ), ); - send_response( - &Response { - inherit: false, - ipc, - metadata: None, - }, - Some(&Payload { + Response::new() + .ipc_bytes(ipc) + .payload(wit::Payload { mime: None, bytes, - }), - ); + }) + .send()?; }, }; }, @@ -179,29 +170,27 @@ fn handle_message ( } } +struct Component; impl Guest for Component { - fn init(our: Address) { - print_to_terminal(1, "key_value_worker: begin"); + fn init(our: String) { + wit::print_to_terminal(1, "key_value_worker: begin"); + let our = Address::from_str(&our).unwrap(); let mut db_handle: Option = None; loop { match handle_message(&our, &mut db_handle) { Ok(()) => {}, Err(e) => { - print_to_terminal(0, format!( + wit::print_to_terminal(0, format!( "key_value_worker: error: {:?}", e, ).as_str()); if let Some(e) = e.downcast_ref::() { - send_response( - &Response { - inherit: false, - ipc: serde_json::to_vec(&e).unwrap(), - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(serde_json::to_vec(&e).unwrap()) + .send() + .unwrap(); } panic!(""); }, diff --git a/modules/key_value/key_value_worker/src/process_lib.rs b/modules/key_value/key_value_worker/src/process_lib.rs deleted file mode 120000 index 9b9ec3f4..00000000 --- a/modules/key_value/key_value_worker/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/process_lib.rs \ No newline at end of file diff --git a/modules/sqlite/sqlite/Cargo.lock b/modules/sqlite/sqlite/Cargo.lock index 510115a5..e120694d 100644 --- a/modules/sqlite/sqlite/Cargo.lock +++ b/modules/sqlite/sqlite/Cargo.lock @@ -23,12 +23,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" @@ -42,28 +36,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "equivalent" @@ -72,12 +48,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "form_urlencoded" -version = "1.2.0" +name = "getrandom" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "percent-encoding", + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -101,16 +79,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" version = "2.0.0" @@ -134,18 +102,18 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - [[package]] name = "num-traits" version = "0.2.17" @@ -162,10 +130,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] -name = "percent-encoding" -version = "2.3.0" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" @@ -176,17 +144,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "quote" version = "1.0.33" @@ -196,6 +153,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rmp" version = "0.8.12" @@ -282,11 +269,11 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "rmp-serde", "serde", "serde_json", "thiserror", + "uqbar_process_lib", "wit-bindgen", ] @@ -321,51 +308,12 @@ dependencies = [ "syn", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -379,40 +327,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand", + "serde", + "wit-bindgen", ] [[package]] -name = "version_check" -version = "0.9.4" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -421,9 +370,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -431,19 +380,17 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ - "bitflags 2.4.0", + "bitflags", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -452,54 +399,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -509,16 +444,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/sqlite/sqlite/Cargo.toml b/modules/sqlite/sqlite/Cargo.toml index a19309d2..3f145102 100644 --- a/modules/sqlite/sqlite/Cargo.toml +++ b/modules/sqlite/sqlite/Cargo.toml @@ -13,12 +13,12 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } rmp-serde = "1.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +uqbar_process_lib = { path = "../../../process_lib" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } [lib] crate-type = ["cdylib"] diff --git a/modules/sqlite/sqlite/src/lib.rs b/modules/sqlite/sqlite/src/lib.rs index 08ea561a..bdff8867 100644 --- a/modules/sqlite/sqlite/src/lib.rs +++ b/modules/sqlite/sqlite/src/lib.rs @@ -1,17 +1,19 @@ -cargo_component_bindings::generate!(); - use std::collections::{HashMap, HashSet}; -use bindings::component::uq_process::types::*; -use bindings::{create_capability, get_capability, Guest, has_capability, print_to_terminal, receive, send_request, send_response, spawn}; +use uqbar_process_lib::{Address, ProcessId, Request, Response}; +use uqbar_process_lib::kernel_types as kt; +use uqbar_process_lib::uqbar::process::standard as wit; + +wit_bindgen::generate!({ + path: "../../../wit", + world: "process", + exports: { + world: Component, + }, +}); -mod kernel_types; -use kernel_types as kt; mod sqlite_types; use sqlite_types as sq; -mod process_lib; - -struct Component; const PREFIX: &str = "sqlite-"; @@ -39,25 +41,20 @@ fn forward_if_have_cap( ipc: Vec, db_to_process: &mut DbToProcess, ) -> anyhow::Result<()> { - if has_capability(&make_db_cap(operation_type, db)) { + if wit::has_capability(&make_db_cap(operation_type, db)) { // forward let Some(process_id) = db_to_process.get(db) else { return Err(sq::SqliteError::DbDoesNotExist.into()); }; - send_request( - &Address { + Request::new() + .target(wit::Address { node: our.node.clone(), process: process_id.clone(), - }, - &Request { - inherit: true, - expects_response: None, - ipc, - metadata: None, - }, - None, - None, - ); + })? + // .target(Address::new(our.node.clone(), process_id.clone()))? + .inherit(true) + .ipc_bytes(ipc) + .send()?; return Ok(()); } else { // reject @@ -71,19 +68,18 @@ fn handle_message ( read_keywords: &HashSet, write_keywords: &HashSet, ) -> anyhow::Result<()> { - let (source, message) = receive().unwrap(); - // let (source, message) = receive()?; + let (source, message) = wit::receive().unwrap(); if our.node != source.node { return Err(sq::SqliteError::RejectForeign.into()); } match message { - Message::Response(_) => { + wit::Message::Response(_) => { return Err(sq::SqliteError::UnexpectedResponse.into()); }, - Message::Request(Request { ipc, .. }) => { - match process_lib::parse_message_ipc(&ipc)? { + wit::Message::Request(wit::Request { ipc, .. }) => { + match serde_json::from_slice(&ipc)? { sq::SqliteMessage::New { ref db } => { // TODO: make atomic // (1): create vfs drive @@ -98,74 +94,59 @@ fn handle_message ( // (1) let vfs_address = Address { node: our.node.clone(), - process: kt::ProcessId::new("vfs", "sys", "uqbar").en_wit(), + process: ProcessId::new("vfs", "sys", "uqbar"), }; let vfs_drive = format!("{}{}", PREFIX, db); - let _ = process_lib::send_and_await_response( - &vfs_address, - false, - serde_json::to_vec(&kt::VfsRequest { + let _ = Request::new() + .target(vfs_address.clone())? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: vfs_drive.clone(), action: kt::VfsAction::New, - }).unwrap(), - None, - None, - 15, - ).unwrap(); + })?) + .expects_response(15) + .send_and_await_response()??; // (2) - let vfs_read = get_capability( + let vfs_read = wit::get_capability( &vfs_address, &make_vfs_cap("read", &vfs_drive), ).ok_or(anyhow::anyhow!("New failed: no vfs 'read' capability found"))?; - let vfs_write = get_capability( + let vfs_write = wit::get_capability( &vfs_address, &make_vfs_cap("write", &vfs_drive), ).ok_or(anyhow::anyhow!("New failed: no vfs 'write' capability found"))?; - let spawned_process_id = match spawn( + let spawned_process_id = match wit::spawn( None, "/sqlite_worker.wasm", - &OnPanic::None, // TODO: notify us - &Capabilities::Some(vec![vfs_read, vfs_write]), + &wit::OnPanic::None, // TODO: notify us + &wit::Capabilities::Some(vec![vfs_read, vfs_write]), false, // not public ) { Ok(spawned_process_id) => spawned_process_id, Err(e) => { - print_to_terminal(0, &format!("couldn't spawn: {}", e)); + wit::print_to_terminal(0, &format!("couldn't spawn: {}", e)); panic!("couldn't spawn"); // TODO }, }; // grant caps - create_capability(&source.process, &make_db_cap("read", db)); - create_capability(&source.process, &make_db_cap("write", db)); + wit::create_capability(&source.process, &make_db_cap("read", db)); + wit::create_capability(&source.process, &make_db_cap("write", db)); // initialize worker - send_request( - &Address { + Request::new() + .target(wit::Address { node: our.node.clone(), process: spawned_process_id.clone(), - }, - &Request { - inherit: false, - expects_response: None, - ipc: ipc.clone(), - metadata: None, - }, - None, - None, - ); + })? + .ipc_bytes(ipc.clone()) + .send()?; // (4) db_to_process.insert(db.into(), spawned_process_id); // TODO: persistence? - send_response( - &Response { - inherit: false, - ipc, - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(ipc) + .send()?; }, sq::SqliteMessage::Write { ref db, ref statement } => { let first_word = statement @@ -196,10 +177,12 @@ fn handle_message ( } } +struct Component; impl Guest for Component { - fn init(our: Address) { - print_to_terminal(0, "sqlite: begin"); + fn init(our: String) { + wit::print_to_terminal(0, "sqlite: begin"); + let our = Address::from_str(&our).unwrap(); let mut db_to_process: DbToProcess = HashMap::new(); let read_keywords: HashSet = [ "ANALYZE", @@ -241,19 +224,15 @@ impl Guest for Component { match handle_message(&our, &mut db_to_process, &read_keywords, &write_keywords) { Ok(()) => {}, Err(e) => { - print_to_terminal(0, format!( + wit::print_to_terminal(0, format!( "sqlite: error: {:?}", e, ).as_str()); if let Some(e) = e.downcast_ref::() { - send_response( - &Response { - inherit: false, - ipc: serde_json::to_vec(&e).unwrap(), - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(serde_json::to_vec(&e).unwrap()) + .send() + .unwrap(); } }, }; diff --git a/modules/sqlite/sqlite_worker/Cargo.lock b/modules/sqlite/sqlite_worker/Cargo.lock index baae6c6b..6ce54ec9 100644 --- a/modules/sqlite/sqlite_worker/Cargo.lock +++ b/modules/sqlite/sqlite_worker/Cargo.lock @@ -40,12 +40,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" @@ -58,30 +52,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#6a2996f280dd8671a2a2d3c83cbe09a39225b526" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] - [[package]] name = "cc" version = "1.0.83" @@ -116,12 +86,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] -name = "form_urlencoded" -version = "1.2.0" +name = "getrandom" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "percent-encoding", + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -158,16 +130,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" version = "2.0.0" @@ -213,12 +175,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - [[package]] name = "num-traits" version = "0.2.17" @@ -240,18 +196,18 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.66" @@ -261,17 +217,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - [[package]] name = "quote" version = "1.0.33" @@ -281,6 +226,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rmp" version = "0.8.12" @@ -308,7 +283,7 @@ name = "rusqlite" version = "0.29.0" source = "git+https://github.com/uqbar-dao/rusqlite?rev=fa6ed84#fa6ed843b65f7dd78d53a1b74c7e5095d71c2bd4" dependencies = [ - "bitflags 2.4.0", + "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -380,12 +355,12 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "cargo-component-bindings", "rmp-serde", "rusqlite", "serde", "serde_json", "thiserror", + "uqbar_process_lib", "wit-bindgen", ] @@ -420,51 +395,12 @@ dependencies = [ "syn", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -478,14 +414,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand", + "serde", + "wit-bindgen", ] [[package]] @@ -501,23 +437,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "wasm-encoder" -version = "0.32.0" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-encoder" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -526,9 +469,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -536,19 +479,17 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ - "bitflags 2.4.0", + "bitflags", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -557,54 +498,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -614,16 +543,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/sqlite/sqlite_worker/Cargo.toml b/modules/sqlite/sqlite_worker/Cargo.toml index d863fbf2..6efab6e0 100644 --- a/modules/sqlite/sqlite_worker/Cargo.toml +++ b/modules/sqlite/sqlite_worker/Cargo.toml @@ -13,13 +13,13 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } rmp-serde = "1.1" rusqlite = { git = "https://github.com/uqbar-dao/rusqlite", rev = "fa6ed84", features = ["bundled", "wasm32-wasi-vfs"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } +uqbar_process_lib = { path = "../../../process_lib" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } [lib] crate-type = ["cdylib"] diff --git a/modules/sqlite/sqlite_worker/src/lib.rs b/modules/sqlite/sqlite_worker/src/lib.rs index d275ab20..50eb5038 100644 --- a/modules/sqlite/sqlite_worker/src/lib.rs +++ b/modules/sqlite/sqlite_worker/src/lib.rs @@ -1,24 +1,24 @@ -cargo_component_bindings::generate!(); - use core::ffi::{c_char, c_int, c_ulonglong, CStr}; use std::ffi::CString; +use rusqlite::{types::FromSql, types::FromSqlError, types::ToSql, types::ValueRef}; + +use uqbar_process_lib::{Address, ProcessId, Response}; +use uqbar_process_lib::uqbar::process::standard as wit; + use crate::sqlite_types::Deserializable; -use rusqlite::{types::FromSql, types::FromSqlError, types::ToSql, types::ValueRef}; -// use serde::{Deserialize, Serialize}; +wit_bindgen::generate!({ + path: "../../../wit", + world: "process", + exports: { + world: Component, + }, +}); -use bindings::component::uq_process::types::*; -use bindings::{get_payload, Guest, print_to_terminal, receive, send_and_await_response, send_response}; - -mod kernel_types; -use kernel_types as kt; -mod process_lib; mod sqlite_types; use sqlite_types as sq; -struct Component; - const PREFIX: &str = "sqlite-"; impl ToSql for sq::SqlValue { @@ -167,11 +167,11 @@ fn from_cbytes_to_vec_u8(bytes: *mut CBytes) -> Vec { bytes } -impl From> for CPrePayload { - fn from(p: Option) -> Self { +impl From> for CPrePayload { + fn from(p: Option) -> Self { let (is_empty, mime, bytes) = match p { None => (0, COptionStr::new(None), CBytes::new_empty()), - Some(Payload { mime, bytes }) => { + Some(wit::Payload { mime, bytes }) => { let mime = match mime { Some(s) => Some(s.as_bytes().to_vec()), None => None, @@ -187,14 +187,14 @@ impl From> for CPrePayload { } } -impl From for Option { +impl From for Option { fn from(p: CPayload) -> Self { if p.is_empty == 0 { None } else { let mime = from_coptionstr_to_option_string(p.mime); let bytes = from_cbytes_to_vec_u8(p.bytes); - Some(Payload { + Some(wit::Payload { mime, bytes, }) @@ -202,13 +202,13 @@ impl From for Option { } } -fn from_cpayload_to_option_payload(p: *const CPayload) -> Option { +fn from_cpayload_to_option_payload(p: *const CPayload) -> Option { if unsafe { (*p).is_empty == 0 } { None } else { let mime = unsafe { from_coptionstr_to_option_string((*p).mime) }; let bytes = unsafe { from_cbytes_to_vec_u8((*p).bytes) }; - Some(Payload { + Some(wit::Payload { mime, bytes, }) @@ -234,7 +234,7 @@ pub extern "C" fn get_payload_wrapped(return_val: *mut CPayload) { // in memory due to an fs bug where chunk size may be bigger than requested let max_len = unsafe { (*(*return_val).bytes).len.clone() }; - let payload = get_payload(); + let payload = wit::get_payload(); let mime_len = { match payload { None => None, @@ -317,13 +317,13 @@ pub extern "C" fn send_and_await_response_wrapped( let request_metadata = from_coptionstr_to_option_string(request_metadata); let ( _, - Message::Response((Response { ipc, metadata, .. }, _)), - ) = send_and_await_response( - &Address { + wit::Message::Response((wit::Response { ipc, metadata, .. }, _)), + ) = wit::send_and_await_response( + &wit::Address { node: target_node, process: target_process, }, - &Request { + &wit::Request { inherit: false, expects_response: Some(timeout), ipc: request_ipc, @@ -346,25 +346,20 @@ pub extern "C" fn send_and_await_response_wrapped( } fn handle_message ( - our: &Address, + our: &wit::Address, db_handle: &mut Option, ) -> anyhow::Result<()> { - let (source, message) = receive().unwrap(); - // let (source, message) = receive()?; + let (source, message) = wit::receive().unwrap(); if our.node != source.node { return Err(sq::SqliteError::RejectForeign.into()); } match message { - Message::Response(_) => { unimplemented!() }, - Message::Request(Request { ipc, .. }) => { - match process_lib::parse_message_ipc(&ipc)? { + wit::Message::Response(_) => { unimplemented!() }, + wit::Message::Request(wit::Request { ipc, .. }) => { + match serde_json::from_slice(&ipc)? { sq::SqliteMessage::New { db } => { - let vfs_address = Address { - node: our.node.clone(), - process: kt::ProcessId::new("vfs", "sys", "uqbar").en_wit(), - }; let vfs_drive = format!("{}{}", PREFIX, db); match db_handle { @@ -391,7 +386,7 @@ fn handle_message ( return Err(sq::SqliteError::DbDoesNotExist.into()); }; - match get_payload() { + match wit::get_payload() { None => { let parameters: Vec<&dyn rusqlite::ToSql> = vec![]; db_handle.execute( @@ -399,7 +394,7 @@ fn handle_message ( ¶meters[..], )?; }, - Some(Payload { mime: _, ref bytes }) => { + Some(wit::Payload { mime: _, ref bytes }) => { let parameters = Vec::::from_serialized(&bytes)?; let parameters: Vec<&dyn rusqlite::ToSql> = parameters .iter() @@ -413,14 +408,9 @@ fn handle_message ( }, } - send_response( - &Response { - inherit: false, - ipc, - metadata: None, - }, - None, - ); + Response::new() + .ipc_bytes(ipc) + .send()?; }, sq::SqliteMessage::Read { ref query, .. } => { let Some(db_handle) = db_handle else { @@ -445,17 +435,13 @@ fn handle_message ( let results = rmp_serde::to_vec(&results).unwrap(); - send_response( - &Response { - inherit: false, - ipc, - metadata: None, - }, - Some(&Payload { + Response::new() + .ipc_bytes(ipc) + .payload(wit::Payload { mime: None, bytes: results, - }), - ); + }) + .send()?; }, } @@ -464,10 +450,12 @@ fn handle_message ( } } +struct Component; impl Guest for Component { - fn init(our: Address) { - print_to_terminal(1, "sqlite_worker: begin"); + fn init(our: String) { + wit::print_to_terminal(1, "sqlite_worker: begin"); + let our = Address::from_str(&our).unwrap(); let mut db_handle: Option = None; loop { @@ -475,10 +463,16 @@ impl Guest for Component { Ok(()) => {}, Err(e) => { // TODO: should we send an error on failure? - print_to_terminal(0, format!( + wit::print_to_terminal(0, format!( "sqlite_worker: error: {:?}", e, ).as_str()); + if let Some(e) = e.downcast_ref::() { + Response::new() + .ipc_bytes(serde_json::to_vec(&e).unwrap()) + .send() + .unwrap(); + } }, }; } From b776d947f5e77f7df42b8e8496bb5916cc606c37 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 20:56:27 -0500 Subject: [PATCH 34/40] update app_store to stdlib --- build.rs | 2 +- modules/app_store/app_store/Cargo.lock | 37 +- modules/app_store/app_store/Cargo.toml | 11 +- modules/app_store/app_store/src/lib.rs | 497 ++++++++---------- modules/app_store/ft_worker/Cargo.lock | 38 +- modules/app_store/ft_worker/Cargo.toml | 9 +- .../app_store/ft_worker/src/ft_worker_lib.rs | 2 +- modules/app_store/ft_worker/src/lib.rs | 8 +- modules/qns_indexer/src/lib.rs | 6 +- process_lib/src/lib.rs | 12 +- 10 files changed, 261 insertions(+), 361 deletions(-) diff --git a/build.rs b/build.rs index e786f72b..d0e7fbfd 100644 --- a/build.rs +++ b/build.rs @@ -153,7 +153,7 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if !["homepage", "http_proxy", "qns_indexer", "terminal"].contains(&package_name) { + if !["app_store", "homepage", "http_proxy", "qns_indexer", "terminal"].contains(&package_name) { continue; } diff --git a/modules/app_store/app_store/Cargo.lock b/modules/app_store/app_store/Cargo.lock index 593f2a85..8793a316 100644 --- a/modules/app_store/app_store/Cargo.lock +++ b/modules/app_store/app_store/Cargo.lock @@ -154,9 +154,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" @@ -232,18 +232,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.190" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd" dependencies = [ "proc-macro2", "quote", @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -330,7 +330,6 @@ dependencies = [ "bincode", "rand", "serde", - "serde_json", "wit-bindgen", ] @@ -348,18 +347,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.10" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", @@ -373,9 +372,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.116.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -384,7 +383,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -393,7 +392,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -403,7 +402,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.13.2" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -415,7 +414,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", diff --git a/modules/app_store/app_store/Cargo.toml b/modules/app_store/app_store/Cargo.toml index 18709cd2..b5b9118e 100644 --- a/modules/app_store/app_store/Cargo.toml +++ b/modules/app_store/app_store/Cargo.toml @@ -13,20 +13,15 @@ lto = true [dependencies] anyhow = "1.0" bincode = "1.3.3" -rand = "0.8.5" +rand = "0.8" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" sha2 = "0.10.8" -wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } uqbar_process_lib = { path = "../../../process_lib" } [lib] crate-type = ["cdylib"] [package.metadata.component] -package = "component:uq-process" - -[package.metadata.component.target] -path = "wit" - -[package.metadata.component.dependencies] +package = "uqbar:process" diff --git a/modules/app_store/app_store/src/lib.rs b/modules/app_store/app_store/src/lib.rs index 40db16b4..c75ea539 100644 --- a/modules/app_store/app_store/src/lib.rs +++ b/modules/app_store/app_store/src/lib.rs @@ -3,10 +3,13 @@ use sha2::Digest; use std::collections::{HashMap, HashSet}; use uqbar_process_lib::kernel_types as kt; use uqbar_process_lib::uqbar::process::standard as wit; -use uqbar_process_lib::{NodeId, Address, ProcessId, Request}; +use uqbar_process_lib::{ + get_capability, get_payload, get_typed_state, grant_messaging, receive, set_state, Address, + Message, NodeId, PackageId, ProcessId, Request, Response, +}; wit_bindgen::generate!({ - path: "../../wit", + path: "../../../wit", world: "process", exports: { world: Component, @@ -173,13 +176,16 @@ impl Guest for Component { ProcessId::from_str("terminal:terminal:uqbar").unwrap(), ]), ); - print_to_terminal(0, &format!("app_store main proc: start")); + println!("app_store main proc: start"); // load in our saved state or initalize a new one if none exists - let mut state = get_typed_state::().unwrap_or(State { - packages: HashMap::new(), - requested_packages: HashSet::new(), - }); + let mut state = get_typed_state(|bytes| { + Ok(bincode::deserialize(bytes).unwrap_or(State { + packages: HashMap::new(), + requested_packages: HashSet::new(), + })) + }) + .unwrap(); // active the main messaging loop: handle requests and responses loop { @@ -187,170 +193,131 @@ impl Guest for Component { Ok((source, message)) => (source, message), Err((error, _context)) => { // TODO handle net errors more usefully based on their context - print_to_terminal(0, &format!("net error: {:?}", error.kind)); + println!("net error: {:?}", error.kind); continue; } }; - match message { - Message::Request(req) => { - match &serde_json::from_slice::(&req.ipc) { - Ok(Req::LocalRequest(local_request)) => { - match handle_local_request(&our, &source, local_request, &mut state) { - Ok(None) => continue, - Ok(Some(resp)) => { - if req.expects_response.is_some() { - send_response( - &Response { - inherit: false, - ipc: serde_json::to_vec(&resp).unwrap(), - metadata: None, - }, - None, - ); - } - } - Err(err) => { - print_to_terminal( - 0, - &format!("app-store: local request error: {:?}", err), - ); - } - } - } - Ok(Req::RemoteRequest(remote_request)) => { - match handle_remote_request(&our, &source, remote_request, &mut state) { - Ok(None) => continue, - Ok(Some(resp)) => { - if req.expects_response.is_some() { - send_response( - &Response { - inherit: false, - ipc: serde_json::to_vec(&resp).unwrap(), - metadata: None, - }, - None, - ); - } - } - Err(err) => { - print_to_terminal( - 0, - &format!("app-store: remote request error: {:?}", err), - ); - } - } - } - Ok(Req::FTWorkerResult(FTWorkerResult::ReceiveSuccess(name))) => { - // do with file what you'd like here - print_to_terminal( - 0, - &format!("file_transfer: successfully received {:?}", name,), - ); - // remove leading / and .zip from file name to get package ID - let package_id = - match PackageId::from_str(name[1..].trim_end_matches(".zip")) { - Ok(package_id) => package_id, - Err(_) => { - print_to_terminal( - 0, - &format!("app store: bad package filename: {}", name), - ); - continue; - } - }; - if state.requested_packages.remove(&package_id) { - // auto-take zip from payload and request ourself with New - let _ = send_request( - &our, - &Request { - inherit: true, // will inherit payload! - expects_response: None, - ipc: serde_json::to_vec(&Req::LocalRequest( - LocalRequest::NewPackage { - package: package_id, - mirror: true, - }, - )) - .unwrap(), - metadata: None, - }, - None, - None, - ); - } - } - Ok(Req::FTWorkerCommand(_)) => { - spawn_receive_transfer(&our, &req.ipc); - } - e => { - print_to_terminal( - 0, - &format!("app store bad request: {:?}, error {:?}", req.ipc, e), - ); - continue; - } - } - } - Message::Response((response, context)) => { - match &serde_json::from_slice::(&response.ipc) { - Ok(Resp::RemoteResponse(remote_response)) => match remote_response { - RemoteResponse::DownloadApproved => { - print_to_terminal( - 0, - "app store: download approved, should be starting", - ); - } - RemoteResponse::DownloadDenied => { - print_to_terminal( - 0, - "app store: could not download package from that node!", - ); - } - }, - Ok(Resp::FTWorkerResult(ft_worker_result)) => { - let Ok(context) = serde_json::from_slice::(&context.unwrap_or_default()) else { - print_to_terminal(0, "file_transfer: got weird local request"); - continue; - }; - match ft_worker_result { - FTWorkerResult::SendSuccess => { - print_to_terminal( - 0, - &format!( - "file_transfer: successfully shared app {} in {:.4}s", - context.file_name, - std::time::SystemTime::now() - .duration_since(context.start_time) - .unwrap() - .as_secs_f64(), - ), - ); - } - e => { - print_to_terminal( - 0, - &format!("app store file transfer: error {:?}", e), - ); - } - } - } - e => { - print_to_terminal( - 0, - &format!( - "app store bad response: {:?}, error {:?}", - response.ipc, e - ), - ); - continue; - } - } - } + match handle_message(&our, &source, &mut state, &message) { + Ok(()) => {} + Err(e) => println!("app-store: error handling message: {:?}", e), } } } } +fn handle_message( + our: &Address, + source: &Address, + mut state: &mut State, + message: &Message, +) -> anyhow::Result<()> { + match message { + Message::Request(req) => { + match &serde_json::from_slice::(&req.ipc) { + Ok(Req::LocalRequest(local_request)) => { + match handle_local_request(&our, &source, local_request, &mut state) { + Ok(None) => return Ok(()), + Ok(Some(resp)) => { + if req.expects_response.is_some() { + Response::new() + .ipc_bytes(serde_json::to_vec(&resp)?) + .send()?; + } + } + Err(err) => { + println!("app-store: local request error: {:?}", err); + } + } + } + Ok(Req::RemoteRequest(remote_request)) => { + match handle_remote_request(&our, &source, remote_request, &mut state) { + Ok(None) => return Ok(()), + Ok(Some(resp)) => { + if req.expects_response.is_some() { + Response::new() + .ipc_bytes(serde_json::to_vec(&resp)?) + .send()?; + } + } + Err(err) => { + println!("app-store: remote request error: {:?}", err); + } + } + } + Ok(Req::FTWorkerResult(FTWorkerResult::ReceiveSuccess(name))) => { + // do with file what you'd like here + println!("file_transfer: successfully received {:?}", name); + // remove leading / and .zip from file name to get package ID + let package_id = match PackageId::from_str(name[1..].trim_end_matches(".zip")) { + Ok(package_id) => package_id, + Err(e) => { + println!("app store: bad package filename: {}", name); + return Err(anyhow::anyhow!(e)); + } + }; + if state.requested_packages.remove(&package_id) { + // auto-take zip from payload and request ourself with New + Request::new() + .target(our.clone())? + .inherit(true) + .ipc_bytes(serde_json::to_vec(&Req::LocalRequest( + LocalRequest::NewPackage { + package: package_id, + mirror: true, + }, + ))?) + .send()?; + } + } + Ok(Req::FTWorkerCommand(_)) => { + spawn_receive_transfer(&our, &req.ipc); + } + e => { + return Err(anyhow::anyhow!( + "app store bad request: {:?}, error {:?}", + req.ipc, + e + )) + } + } + } + Message::Response((response, context)) => { + match &serde_json::from_slice::(&response.ipc) { + Ok(Resp::RemoteResponse(remote_response)) => match remote_response { + RemoteResponse::DownloadApproved => { + println!("app store: download approved, should be starting"); + } + RemoteResponse::DownloadDenied => { + println!("app store: could not download package from that node!"); + } + }, + Ok(Resp::FTWorkerResult(ft_worker_result)) => { + let Ok(context) = + serde_json::from_slice::(&context.as_ref().unwrap()) + else { + return Err(anyhow::anyhow!("file_transfer: got weird local request")); + }; + match ft_worker_result { + FTWorkerResult::SendSuccess => { + println!( + "file_transfer: successfully shared app {} in {:.4}s", + context.file_name, + std::time::SystemTime::now() + .duration_since(context.start_time) + .unwrap() + .as_secs_f64(), + ); + } + e => return Err(anyhow::anyhow!("file_transfer: {:?}", e)), + } + } + _ => return Err(anyhow::anyhow!("bad response from file transfer worker")), + } + } + } + Ok(()) +} + fn handle_local_request( our: &Address, source: &Address, @@ -362,76 +329,62 @@ fn handle_local_request( } match request { LocalRequest::NewPackage { package, mirror } => { - let Some(mut payload) = get_payload() else { - return Err(anyhow::anyhow!("no payload")); - }; let vfs_address = Address { node: our.node.clone(), process: ProcessId::from_str("vfs:sys:uqbar")?, }; + Request::new() + .target(vfs_address.clone())? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { + drive: package.to_string(), + action: kt::VfsAction::New, + })?) + .send_and_await_response(5)??; + + let Some(mut payload) = get_payload() else { + return Err(anyhow::anyhow!("no payload")); + }; // produce the version hash for this new package let mut hasher = sha2::Sha256::new(); hasher.update(&payload.bytes); let version_hash = format!("{:x}", hasher.finalize()); - send_and_await_typed_response( - &vfs_address, - false, - &kt::VfsRequest { - drive: package.to_string(), - action: kt::VfsAction::New, - }, - None, - None, - 5, - )??; - // add zip bytes payload.mime = Some("application/zip".to_string()); - send_and_await_typed_response( - &vfs_address, - true, - &kt::VfsRequest { + Request::new() + .target(vfs_address.clone())? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::Add { full_path: package.to_string(), entry_type: kt::AddEntryType::ZipArchive, }, - }, - None, - Some(&payload), - 5, - )??; + })?) + .payload(payload.clone()) + .send_and_await_response(5)??; // save the zip file itself in VFS for sharing with other nodes // call it .zip - send_and_await_typed_response( - &vfs_address, - true, - &kt::VfsRequest { + Request::new() + .target(vfs_address.clone())? + .inherit(true) + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::Add { full_path: format!("/{}.zip", package.to_string()), entry_type: kt::AddEntryType::NewFile, }, - }, - None, - Some(&payload), - 5, - )??; - - send_and_await_typed_response( - &vfs_address, - false, - &kt::VfsRequest { + })?) + .payload(payload) + .send_and_await_response(5)??; + Request::new() + .target(vfs_address.clone())? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry("/metadata.json".into()), - }, - None, - None, - 5, - )??; + })?) + .send_and_await_response(5)??; let Some(payload) = get_payload() else { return Err(anyhow::anyhow!("no metadata payload")); }; @@ -453,30 +406,27 @@ fn handle_local_request( auto_update: true, }; state.packages.insert(package.clone(), package_state); - set_typed_state::(&state); + crate::set_state(&bincode::serialize(state)?); Ok(Some(Resp::NewPackageResponse(NewPackageResponse::Success))) } LocalRequest::Download { package, install_from, } => Ok(Some(Resp::DownloadResponse( - match send_and_await_typed_response( - &Address { - node: install_from.clone(), - process: our.process.clone(), - }, - true, - &RemoteRequest::Download(package.clone()), - None, - None, - 5, - ) { + match Request::new() + .target(Address::new(&install_from, our.process.clone())?)? + .inherit(true) + .ipc_bytes(serde_json::to_vec(&RemoteRequest::Download( + package.clone(), + ))?) + .send_and_await_response(5) + { Ok(Ok((_source, Message::Response((resp, _context))))) => { let resp = serde_json::from_slice::(&resp.ipc)?; match resp { Resp::RemoteResponse(RemoteResponse::DownloadApproved) => { state.requested_packages.insert(package.clone()); - set_typed_state::(&state); + crate::set_state(&bincode::serialize(&state)?); DownloadResponse::Started } _ => DownloadResponse::Failure, @@ -490,17 +440,13 @@ fn handle_local_request( node: our.node.clone(), process: ProcessId::from_str("vfs:sys:uqbar")?, }; - send_and_await_typed_response( - &vfs_address, - false, - &kt::VfsRequest { + Request::new() + .target(Address::new(&our.node, "vfs:sys:uqbar")?)? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry("/manifest.json".into()), - }, - None, - None, - 5, - )??; + })?) + .send_and_await_response(5)??; let Some(payload) = get_payload() else { return Err(anyhow::anyhow!("no payload")); }; @@ -513,19 +459,15 @@ fn handle_local_request( format!("/{}", entry.process_wasm_path) }; - let (_, hash_response) = send_and_await_typed_response( - &vfs_address, - false, - &kt::VfsRequest { + let (_, hash_response) = Request::new() + .target(Address::new(&our.node, "vfs:sys:uqbar")?)? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetHash(path.clone()), - }, - None, - None, - 5, - )??; + })?) + .send_and_await_response(5)??; - let Message::Response((Response { ipc, .. }, _)) = hash_response else { + let Message::Response((wit::Response { ipc, .. }, _)) = hash_response else { return Err(anyhow::anyhow!("bad vfs response")); }; let kt::VfsResponse::GetHash(Some(hash)) = serde_json::from_slice(&ipc)? else { @@ -577,9 +519,9 @@ fn handle_local_request( node: our.node.clone(), process: parsed_process_id.clone(), }, - &"\"messaging\"".into() + &"\"messaging\"".into(), ) else { - print_to_terminal(0, &format!("app-store: no cap for {} to give away!", process_name)); + println!("app-store: no cap for {} to give away!", process_name); continue; }; initial_capabilities.insert(kt::de_wit_signed_capability(messaging_cap)); @@ -589,63 +531,46 @@ fn handle_local_request( let Ok(parsed_new_process_id) = ProcessId::from_str(&process_id) else { return Err(anyhow::anyhow!("app-store: invalid process id!")); }; - send_typed_request( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("kernel:sys:uqbar")?, - }, - false, - &kt::KernelCommand::KillProcess(kt::ProcessId::de_wit( - parsed_new_process_id.clone(), - )), - None, - None, - None, - ); + Request::new() + .target(Address::new(&our.node, "kernel:sys:uqbar")?)? + .ipc_bytes(serde_json::to_vec(&kt::KernelCommand::KillProcess( + kt::ProcessId::de_wit(parsed_new_process_id.clone()), + ))?) + .send()?; // kernel start process takes bytes as payload + wasm_bytes_handle... // reconsider perhaps - let (_, _bytes_response) = send_and_await_typed_response( - &vfs_address, - false, - &kt::VfsRequest { + let (_, _bytes_response) = Request::new() + .target(Address::new(&our.node, "vfs:sys:uqbar")?)? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry(path), - }, - None, - None, - 5, - )??; + })?) + .send_and_await_response(5)??; let Some(payload) = get_payload() else { return Err(anyhow::anyhow!("no wasm bytes payload.")); }; - send_and_await_typed_response( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("kernel:sys:uqbar")?, - }, - false, - &kt::KernelCommand::StartProcess { + Request::new() + .target(Address::new(&our.node, "kernel:sys:uqbar")?)? + .ipc_bytes(serde_json::to_vec(&kt::KernelCommand::StartProcess { id: kt::ProcessId::de_wit(parsed_new_process_id), wasm_bytes_handle: hash, on_panic: entry.on_panic, initial_capabilities, public: entry.public, - }, - None, - Some(&payload), - 5, - )??; + })?) + .payload(payload) + .send_and_await_response(5)?; } Ok(Some(Resp::InstallResponse(InstallResponse::Success))) } - LocalRequest::Uninstall(package) => { + LocalRequest::Uninstall(_package) => { // TODO Ok(None) } - LocalRequest::Delete(package) => { + LocalRequest::Delete(_package) => { // TODO Ok(None) } @@ -661,28 +586,20 @@ fn handle_remote_request( match request { RemoteRequest::Download(package) => { let Some(package_state) = state.packages.get(&package) else { - return Ok(Some(Resp::RemoteResponse(RemoteResponse::DownloadDenied))) + return Ok(Some(Resp::RemoteResponse(RemoteResponse::DownloadDenied))); }; if !package_state.mirroring { return Ok(Some(Resp::RemoteResponse(RemoteResponse::DownloadDenied))); } // get the .zip from VFS and attach as payload to response - let vfs_address = Address { - node: our.node.clone(), - process: ProcessId::from_str("vfs:sys:uqbar")?, - }; let file_name = format!("/{}.zip", package.to_string()); - send_and_await_typed_response( - &vfs_address, - false, - &kt::VfsRequest { + Request::new() + .target(Address::new(&our.node, "vfs:sys:uqbar")?)? + .ipc_bytes(serde_json::to_vec(&kt::VfsRequest { drive: package.to_string(), action: kt::VfsAction::GetEntry(file_name.clone()), - }, - None, - None, - 5, - )??; + })?) + .send_and_await_response(5)?; // transfer will inherit the payload bytes we receive from VFS spawn_transfer(&our, &file_name, None, &source); Ok(Some(Resp::RemoteResponse(RemoteResponse::DownloadApproved))) diff --git a/modules/app_store/ft_worker/Cargo.lock b/modules/app_store/ft_worker/Cargo.lock index 896c5fa8..5a6cda87 100644 --- a/modules/app_store/ft_worker/Cargo.lock +++ b/modules/app_store/ft_worker/Cargo.lock @@ -105,9 +105,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" @@ -183,18 +183,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.190" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd" dependencies = [ "proc-macro2", "quote", @@ -229,9 +229,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -262,8 +262,8 @@ version = "0.2.0" dependencies = [ "anyhow", "bincode", + "rand", "serde", - "serde_json", "wit-bindgen", ] @@ -275,18 +275,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.10" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.116.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -311,7 +311,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -320,7 +320,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -330,7 +330,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.13.2" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", @@ -342,7 +342,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.13.1" -source = "git+https://github.com/bytecodealliance/wit-bindgen#5390bab780733f1660d14c254ec985df2816bf1d" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", diff --git a/modules/app_store/ft_worker/Cargo.toml b/modules/app_store/ft_worker/Cargo.toml index 5d9a3b60..42bded6a 100644 --- a/modules/app_store/ft_worker/Cargo.toml +++ b/modules/app_store/ft_worker/Cargo.toml @@ -16,16 +16,11 @@ bincode = "1.3.3" rand = "0.8" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.13.0" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } uqbar_process_lib = { path = "../../../process_lib" } [lib] crate-type = ["cdylib"] [package.metadata.component] -package = "component:uq-process" - -[package.metadata.component.target] -path = "wit" - -[package.metadata.component.dependencies] +package = "uqbar:process" diff --git a/modules/app_store/ft_worker/src/ft_worker_lib.rs b/modules/app_store/ft_worker/src/ft_worker_lib.rs index a960fa66..70b9c9e5 100644 --- a/modules/app_store/ft_worker/src/ft_worker_lib.rs +++ b/modules/app_store/ft_worker/src/ft_worker_lib.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use uqbar_process_lib::component::uq_process::api::*; +use uqbar_process_lib::uqbar::process::standard::*; #[derive(Debug, Serialize, Deserialize)] pub struct FileTransferContext { diff --git a/modules/app_store/ft_worker/src/lib.rs b/modules/app_store/ft_worker/src/lib.rs index e4aaaadd..b1d33254 100644 --- a/modules/app_store/ft_worker/src/lib.rs +++ b/modules/app_store/ft_worker/src/lib.rs @@ -1,20 +1,17 @@ use serde::{Deserialize, Serialize}; -use uqbar_process_lib::component::uq_process::api::*; -use uqbar_process_lib::component::uq_process::types::SendErrorKind; +use uqbar_process_lib::uqbar::process::standard::*; mod ft_worker_lib; use ft_worker_lib::*; wit_bindgen::generate!({ path: "../../../wit", - world: "uq-process", + world: "process", exports: { world: Component, }, }); -struct Component; - /// internal worker protocol #[derive(Debug, Serialize, Deserialize)] pub enum FTWorkerProtocol { @@ -22,6 +19,7 @@ pub enum FTWorkerProtocol { Finished, } +struct Component; impl Guest for Component { fn init(our: String) { let our = Address::from_str(&our).unwrap(); diff --git a/modules/qns_indexer/src/lib.rs b/modules/qns_indexer/src/lib.rs index 21f11798..3e2ef1ca 100644 --- a/modules/qns_indexer/src/lib.rs +++ b/modules/qns_indexer/src/lib.rs @@ -6,7 +6,7 @@ use serde_json::json; use std::collections::HashMap; use std::string::FromUtf8Error; use uqbar_process_lib::{ - get_typed_state, receive, set_state, Address, Message, Payload, ProcessId, Request, + get_typed_state, receive, set_state, Address, Message, Payload, Request, Response, }; @@ -107,10 +107,6 @@ fn serialize_message(message: &NetActions) -> anyhow::Result> { Ok(serde_json::to_vec(message)?) } -fn deserialize_message(bytes: &[u8]) -> anyhow::Result { - Ok(serde_json::from_slice(bytes)?) -} - fn serialize_json_message(message: &serde_json::Value) -> anyhow::Result> { Ok(serde_json::to_vec(message)?) } diff --git a/process_lib/src/lib.rs b/process_lib/src/lib.rs index aefd67b9..aa744237 100644 --- a/process_lib/src/lib.rs +++ b/process_lib/src/lib.rs @@ -350,8 +350,8 @@ impl Request { Ok(self) } - pub fn metadata(mut self, metadata: Option) -> Self { - self.metadata = metadata; + pub fn metadata(mut self, metadata: String) -> Self { + self.metadata = Some(metadata); self } @@ -389,8 +389,8 @@ impl Request { } } - pub fn context_bytes(mut self, context: Option>) -> Self { - self.context = context; + pub fn context_bytes(mut self, context: Vec) -> Self { + self.context = Some(context); self } @@ -421,13 +421,13 @@ impl Request { } } - pub fn send_and_await_response(self) -> anyhow::Result> { + pub fn send_and_await_response(self, timeout: u64) -> anyhow::Result> { if let (Some(target), Some(ipc)) = (self.target, self.ipc) { Ok(crate::send_and_await_response( &target, &wit::Request { inherit: self.inherit, - expects_response: self.timeout, + expects_response: Some(timeout), ipc, metadata: self.metadata, }, From 07dd5f0c64a0524a2f2a29a40a16d2ea1a21fe1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 01:57:34 +0000 Subject: [PATCH 35/40] Format Rust code using rustfmt --- build.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index d0e7fbfd..c6ec1fa3 100644 --- a/build.rs +++ b/build.rs @@ -153,7 +153,15 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if !["app_store", "homepage", "http_proxy", "qns_indexer", "terminal"].contains(&package_name) { + if ![ + "app_store", + "homepage", + "http_proxy", + "qns_indexer", + "terminal", + ] + .contains(&package_name) + { continue; } From 3cff7a155cbcb12d98e9cf7239e0bb5c296fc99e Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 21:01:57 -0500 Subject: [PATCH 36/40] remove old symlinks, send_and_await takes timeout --- build.rs | 2 ++ modules/key_value/key_value/src/lib.rs | 3 +-- modules/sqlite/sqlite/src/kernel_types.rs | 1 - modules/sqlite/sqlite/src/lib.rs | 3 +-- modules/sqlite/sqlite/src/process_lib.rs | 1 - modules/sqlite/sqlite_worker/src/kernel_types.rs | 1 - modules/sqlite/sqlite_worker/src/process_lib.rs | 1 - 7 files changed, 4 insertions(+), 8 deletions(-) delete mode 120000 modules/sqlite/sqlite/src/kernel_types.rs delete mode 120000 modules/sqlite/sqlite/src/process_lib.rs delete mode 120000 modules/sqlite/sqlite_worker/src/kernel_types.rs delete mode 120000 modules/sqlite/sqlite_worker/src/process_lib.rs diff --git a/build.rs b/build.rs index c6ec1fa3..949ebb60 100644 --- a/build.rs +++ b/build.rs @@ -157,7 +157,9 @@ fn main() { "app_store", "homepage", "http_proxy", + "key_value", "qns_indexer", + "sqlite", "terminal", ] .contains(&package_name) diff --git a/modules/key_value/key_value/src/lib.rs b/modules/key_value/key_value/src/lib.rs index 70c1dc19..a9486cf0 100644 --- a/modules/key_value/key_value/src/lib.rs +++ b/modules/key_value/key_value/src/lib.rs @@ -101,8 +101,7 @@ fn handle_message(our: &Address, db_to_process: &mut DbToProcess) -> anyhow::Res drive: vfs_drive.clone(), action: kt::VfsAction::New, })?) - .expects_response(15) - .send_and_await_response()??; + .send_and_await_response(15)??; // (2) let vfs_read = wit::get_capability(&vfs_address, &make_vfs_cap("read", &vfs_drive)) diff --git a/modules/sqlite/sqlite/src/kernel_types.rs b/modules/sqlite/sqlite/src/kernel_types.rs deleted file mode 120000 index 047e48bc..00000000 --- a/modules/sqlite/sqlite/src/kernel_types.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/kernel_types.rs \ No newline at end of file diff --git a/modules/sqlite/sqlite/src/lib.rs b/modules/sqlite/sqlite/src/lib.rs index bdff8867..d2a19b37 100644 --- a/modules/sqlite/sqlite/src/lib.rs +++ b/modules/sqlite/sqlite/src/lib.rs @@ -103,8 +103,7 @@ fn handle_message ( drive: vfs_drive.clone(), action: kt::VfsAction::New, })?) - .expects_response(15) - .send_and_await_response()??; + .send_and_await_response(15)??; // (2) let vfs_read = wit::get_capability( diff --git a/modules/sqlite/sqlite/src/process_lib.rs b/modules/sqlite/sqlite/src/process_lib.rs deleted file mode 120000 index 9b9ec3f4..00000000 --- a/modules/sqlite/sqlite/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/process_lib.rs \ No newline at end of file diff --git a/modules/sqlite/sqlite_worker/src/kernel_types.rs b/modules/sqlite/sqlite_worker/src/kernel_types.rs deleted file mode 120000 index 047e48bc..00000000 --- a/modules/sqlite/sqlite_worker/src/kernel_types.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/kernel_types.rs \ No newline at end of file diff --git a/modules/sqlite/sqlite_worker/src/process_lib.rs b/modules/sqlite/sqlite_worker/src/process_lib.rs deleted file mode 120000 index 9b9ec3f4..00000000 --- a/modules/sqlite/sqlite_worker/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../src/process_lib.rs \ No newline at end of file From ab198786549c24f4cd779ce81bb2a5094c3f88c2 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 22:04:50 -0500 Subject: [PATCH 37/40] chess compiling --- build.rs | 1 + modules/chess/Cargo-component.lock | 3 - modules/chess/Cargo.lock | 290 +++--- modules/chess/Cargo.toml | 15 +- modules/chess/src/chess.html | 17 - modules/chess/src/index.css | 1 - modules/chess/src/index.js | 113 --- modules/chess/src/lib.rs | 1400 +++++++++++++--------------- modules/chess/src/process_lib.rs | 1 - 9 files changed, 751 insertions(+), 1090 deletions(-) delete mode 100644 modules/chess/Cargo-component.lock delete mode 100644 modules/chess/src/chess.html delete mode 100644 modules/chess/src/index.css delete mode 100644 modules/chess/src/index.js delete mode 120000 modules/chess/src/process_lib.rs diff --git a/build.rs b/build.rs index 949ebb60..6f0fb28b 100644 --- a/build.rs +++ b/build.rs @@ -155,6 +155,7 @@ fn main() { if ![ "app_store", + "chess", "homepage", "http_proxy", "key_value", diff --git a/modules/chess/Cargo-component.lock b/modules/chess/Cargo-component.lock deleted file mode 100644 index 00bc239d..00000000 --- a/modules/chess/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/modules/chess/Cargo.lock b/modules/chess/Cargo.lock index 0d1de6c1..da1f5d51 100644 --- a/modules/chess/Cargo.lock +++ b/modules/chess/Cargo.lock @@ -46,33 +46,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#36c221e41db3e87dec4c82eadcb9bc8f37626533" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#36c221e41db3e87dec4c82eadcb9bc8f37626533" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "cfg-if" @@ -87,10 +63,10 @@ dependencies = [ "anyhow", "base64", "bincode", - "cargo-component-bindings", "pleco", "serde", "serde_json", + "uqbar_process_lib", "wit-bindgen", ] @@ -148,15 +124,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -164,10 +131,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] -name = "hashbrown" -version = "0.14.0" +name = "getrandom" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -190,21 +168,11 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", @@ -231,9 +199,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" @@ -241,12 +209,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - [[package]] name = "memoffset" version = "0.9.0" @@ -272,12 +234,6 @@ dependencies = [ "libc", ] -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - [[package]] name = "pleco" version = "0.5.0" @@ -288,28 +244,23 @@ dependencies = [ "lazy_static", "mucow", "num_cpus", - "rand", + "rand 0.6.5", "rayon", ] [[package]] -name = "proc-macro2" -version = "1.0.66" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "pulldown-cmark" -version = "0.9.3" +name = "proc-macro2" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", + "unicode-ident", ] [[package]] @@ -329,7 +280,7 @@ checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ "autocfg 0.1.8", "libc", - "rand_chacha", + "rand_chacha 0.1.1", "rand_core 0.4.2", "rand_hc", "rand_isaac", @@ -340,6 +291,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.1.1" @@ -350,6 +312,16 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -365,6 +337,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rand_hc" version = "0.1.0" @@ -470,24 +451,24 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd" dependencies = [ "proc-macro2", "quote", @@ -496,9 +477,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -507,9 +488,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "spdx" @@ -522,59 +503,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" @@ -589,40 +531,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +name = "uqbar_process_lib" +version = "0.2.0" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "anyhow", + "bincode", + "rand 0.8.5", + "serde", + "wit-bindgen", ] [[package]] -name = "version_check" -version = "0.9.4" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.3" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" +checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f" dependencies = [ "anyhow", "indexmap", "serde", + "serde_derive", "serde_json", "spdx", "wasm-encoder", @@ -631,9 +574,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.112.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ "indexmap", "semver", @@ -663,19 +606,17 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "wit-component", @@ -684,54 +625,42 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +version = "0.13.2" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", - "wit-bindgen-rust-lib", "wit-component", ] -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +version = "0.13.1" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d" dependencies = [ "anyhow", "proc-macro2", + "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" +checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" dependencies = [ "anyhow", - "bitflags 2.4.0", + "bitflags 2.4.1", "indexmap", "log", "serde", + "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", @@ -741,16 +670,17 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" dependencies = [ "anyhow", "id-arena", "indexmap", "log", - "pulldown-cmark", "semver", + "serde", + "serde_derive", + "serde_json", "unicode-xid", - "url", ] diff --git a/modules/chess/Cargo.toml b/modules/chess/Cargo.toml index b51284e0..3a200986 100644 --- a/modules/chess/Cargo.toml +++ b/modules/chess/Cargo.toml @@ -12,21 +12,16 @@ lto = true [dependencies] anyhow = "1.0" +base64 = "0.13" bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } +pleco = "0.5" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } -base64 = "0.13" -pleco = "0.5" +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } +uqbar_process_lib = { path = "../../process_lib" } [lib] crate-type = ["cdylib"] [package.metadata.component] -package = "component:uq-process" - -[package.metadata.component.target] -path = "wit" - -[package.metadata.component.dependencies] +package = "uqbar:process" diff --git a/modules/chess/src/chess.html b/modules/chess/src/chess.html deleted file mode 100644 index d2196de7..00000000 --- a/modules/chess/src/chess.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - Chess - - - -
- - - - diff --git a/modules/chess/src/index.css b/modules/chess/src/index.css deleted file mode 100644 index 9ac42996..00000000 --- a/modules/chess/src/index.css +++ /dev/null @@ -1 +0,0 @@ -#root{max-width:1280px;margin:0 auto;text-align:center;width:100%}.logo{height:6em;padding:1.5em;will-change:filter;transition:filter .3s}.logo:hover{filter:drop-shadow(0 0 2em #646cffaa)}.logo.react:hover{filter:drop-shadow(0 0 2em #61dafbaa)}@keyframes logo-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion: no-preference){a:nth-of-type(2) .logo{animation:logo-spin infinite 20s linear}}.card{padding:2em}.read-the-docs{color:#888}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.left-0{left:0px}.top-6{top:1.5rem}.m-2{margin:.5rem}.m-4{margin:1rem}.mb-2{margin-bottom:.5rem}.mb-40{margin-bottom:10rem}.flex{display:flex}.h-screen{height:100vh}.w-full{width:100%}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.overflow-scroll{overflow:scroll}.rounded{border-radius:.25rem}.border{border-width:1px}.border-r{border-right-width:1px}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity))}.p-2{padding:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pb-2{padding-bottom:.5rem}.font-bold{font-weight:700}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%;width:100%;--uq-vlightpurple: #d9dcfc;--uq-lightpurple: #c7cafa;--uq-purple: #727bf2;--uq-darkpurple: #5761ef;--midnightpurp: #0a1170;--forgottenpurp: #45475e;--uq-lightpink: #f3ced2;--uq-pink: #dd7c8a;--uq-darkpink: #cd3c52;--blush: #d55d6f;--celeste: #adebe5;--lturq: #6bdbd0;--turq: #3acfc0;--celadon: #21897e;--deep-jungle: #14524c;--old-mint: #659792;--washed-gray: rgba(0, 0, 0, .03);--light-gray: rgba(0, 0, 0, .1);--medium-gray: rgba(0, 0, 0, .2);--dark-gray: rgba(0, 0, 0, .5);--charcoal: #333}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;min-width:320px;min-height:100vh;color:#fff;background-color:var(--midnightpurp)}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}h1,h2,h3,h4,h5,h6{font-weight:600}h1{font-size:3em}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1em}@keyframes colorChange{0%,to{background-color:var(--uq-purple)}50%{background-color:var(--uq-pink)}}.game-entry{width:calc(100% - 1em);cursor:pointer;color:#fff;background-color:var(--uq-purple);padding:.5em;border-radius:.25em}.game-entry.is-turn{animation:colorChange 3s infinite}.game-entry.selected{background-color:var(--uq-darkpink)}.game-entry.is-ended{background-color:#d3d3d3;color:gray}.game-entry:hover{background-color:var(--uq-pink)}.hover\:bg-green-800:hover{--tw-bg-opacity: 1;background-color:rgb(22 101 52 / var(--tw-bg-opacity))} diff --git a/modules/chess/src/index.js b/modules/chess/src/index.js deleted file mode 100644 index 679aa4d4..00000000 --- a/modules/chess/src/index.js +++ /dev/null @@ -1,113 +0,0 @@ -var fv=Object.defineProperty;var dv=(e,t,r)=>t in e?fv(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var ft=(e,t,r)=>(dv(e,typeof t!="symbol"?t+"":t,r),r);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&n(s)}).observe(document,{childList:!0,subtree:!0});function r(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function n(i){if(i.ep)return;i.ep=!0;const a=r(i);fetch(i.href,a)}})();var hv=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Qc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function pv(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var r=function n(){if(this instanceof n){var i=[null];i.push.apply(i,arguments);var a=Function.bind.apply(t,i);return new a}return t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(n){var i=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,i.get?i:{enumerable:!0,get:function(){return e[n]}})}),r}var Lh={exports:{}},il={},Oh={exports:{}},we={};/** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ts=Symbol.for("react.element"),gv=Symbol.for("react.portal"),vv=Symbol.for("react.fragment"),yv=Symbol.for("react.strict_mode"),mv=Symbol.for("react.profiler"),Cv=Symbol.for("react.provider"),Ev=Symbol.for("react.context"),Sv=Symbol.for("react.forward_ref"),xv=Symbol.for("react.suspense"),Tv=Symbol.for("react.memo"),wv=Symbol.for("react.lazy"),E0=Symbol.iterator;function Iv(e){return e===null||typeof e!="object"?null:(e=E0&&e[E0]||e["@@iterator"],typeof e=="function"?e:null)}var Ph={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Uh=Object.assign,Mh={};function ha(e,t,r){this.props=e,this.context=t,this.refs=Mh,this.updater=r||Ph}ha.prototype.isReactComponent={};ha.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};ha.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Fh(){}Fh.prototype=ha.prototype;function Wc(e,t,r){this.props=e,this.context=t,this.refs=Mh,this.updater=r||Ph}var Yc=Wc.prototype=new Fh;Yc.constructor=Wc;Uh(Yc,ha.prototype);Yc.isPureReactComponent=!0;var S0=Array.isArray,Vh=Object.prototype.hasOwnProperty,Xc={current:null},jh={key:!0,ref:!0,__self:!0,__source:!0};function Kh(e,t,r){var n,i={},a=null,s=null;if(t!=null)for(n in t.ref!==void 0&&(s=t.ref),t.key!==void 0&&(a=""+t.key),t)Vh.call(t,n)&&!jh.hasOwnProperty(n)&&(i[n]=t[n]);var o=arguments.length-2;if(o===1)i.children=r;else if(1>>1,ie=W[ee];if(0>>1;eei(Fe,ne))Rei(ke,Fe)?(W[ee]=ke,W[Re]=ne,ee=Re):(W[ee]=Fe,W[se]=ne,ee=se);else if(Rei(ke,ne))W[ee]=ke,W[Re]=ne,ee=Re;else break e}}return ae}function i(W,ae){var ne=W.sortIndex-ae.sortIndex;return ne!==0?ne:W.id-ae.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var s=Date,o=s.now();e.unstable_now=function(){return s.now()-o}}var l=[],u=[],c=1,f=null,d=3,v=!1,g=!1,C=!1,I=typeof setTimeout=="function"?setTimeout:null,y=typeof clearTimeout=="function"?clearTimeout:null,m=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function S(W){for(var ae=r(u);ae!==null;){if(ae.callback===null)n(u);else if(ae.startTime<=W)n(u),ae.sortIndex=ae.expirationTime,t(l,ae);else break;ae=r(u)}}function B(W){if(C=!1,S(W),!g)if(r(l)!==null)g=!0,Le(R);else{var ae=r(u);ae!==null&&te(B,ae.startTime-W)}}function R(W,ae){g=!1,C&&(C=!1,y(V),V=-1),v=!0;var ne=d;try{for(S(ae),f=r(l);f!==null&&(!(f.expirationTime>ae)||W&&!ce());){var ee=f.callback;if(typeof ee=="function"){f.callback=null,d=f.priorityLevel;var ie=ee(f.expirationTime<=ae);ae=e.unstable_now(),typeof ie=="function"?f.callback=ie:f===r(l)&&n(l),S(ae)}else n(l);f=r(l)}if(f!==null)var Oe=!0;else{var se=r(u);se!==null&&te(B,se.startTime-ae),Oe=!1}return Oe}finally{f=null,d=ne,v=!1}}var L=!1,M=null,V=-1,Q=5,G=-1;function ce(){return!(e.unstable_now()-GW||125ee?(W.sortIndex=ne,t(u,W),r(l)===null&&W===r(u)&&(C?(y(V),V=-1):C=!0,te(B,ne-ee))):(W.sortIndex=ie,t(l,W),g||v||(g=!0,Le(R))),W},e.unstable_shouldYield=ce,e.unstable_wrapCallback=function(W){var ae=d;return function(){var ne=d;d=ae;try{return W.apply(this,arguments)}finally{d=ne}}}})($h);qh.exports=$h;var Uv=qh.exports;/** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Gh=q,cr=Uv;function H(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,r=1;r"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Nu=Object.prototype.hasOwnProperty,Mv=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,T0={},w0={};function Fv(e){return Nu.call(w0,e)?!0:Nu.call(T0,e)?!1:Mv.test(e)?w0[e]=!0:(T0[e]=!0,!1)}function Vv(e,t,r,n){if(r!==null&&r.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return n?!1:r!==null?!r.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function jv(e,t,r,n){if(t===null||typeof t>"u"||Vv(e,t,r,n))return!0;if(n)return!1;if(r!==null)switch(r.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function $t(e,t,r,n,i,a,s){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=n,this.attributeNamespace=i,this.mustUseProperty=r,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=s}var Rt={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){Rt[e]=new $t(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];Rt[t]=new $t(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){Rt[e]=new $t(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){Rt[e]=new $t(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){Rt[e]=new $t(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){Rt[e]=new $t(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){Rt[e]=new $t(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){Rt[e]=new $t(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){Rt[e]=new $t(e,5,!1,e.toLowerCase(),null,!1,!1)});var Jc=/[\-:]([a-z])/g;function ef(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Jc,ef);Rt[t]=new $t(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Jc,ef);Rt[t]=new $t(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Jc,ef);Rt[t]=new $t(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){Rt[e]=new $t(e,1,!1,e.toLowerCase(),null,!1,!1)});Rt.xlinkHref=new $t("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){Rt[e]=new $t(e,1,!1,e.toLowerCase(),null,!0,!0)});function tf(e,t,r,n){var i=Rt.hasOwnProperty(t)?Rt[t]:null;(i!==null?i.type!==0:n||!(2o||i[s]!==a[o]){var l=` -`+i[s].replace(" at new "," at ");return e.displayName&&l.includes("")&&(l=l.replace("",e.displayName)),l}while(1<=s&&0<=o);break}}}finally{jl=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?Oa(e):""}function Kv(e){switch(e.tag){case 5:return Oa(e.type);case 16:return Oa("Lazy");case 13:return Oa("Suspense");case 19:return Oa("SuspenseList");case 0:case 2:case 15:return e=Kl(e.type,!1),e;case 11:return e=Kl(e.type.render,!1),e;case 1:return e=Kl(e.type,!0),e;default:return""}}function Ou(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Pi:return"Fragment";case Oi:return"Portal";case Du:return"Profiler";case rf:return"StrictMode";case Ru:return"Suspense";case Lu:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Yh:return(e.displayName||"Context")+".Consumer";case Wh:return(e._context.displayName||"Context")+".Provider";case nf:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case af:return t=e.displayName||null,t!==null?t:Ou(e.type)||"Memo";case An:t=e._payload,e=e._init;try{return Ou(e(t))}catch{}}return null}function zv(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Ou(t);case 8:return t===rf?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function Wn(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Zh(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Hv(e){var t=Zh(e)?"checked":"value",r=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof r<"u"&&typeof r.get=="function"&&typeof r.set=="function"){var i=r.get,a=r.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return i.call(this)},set:function(s){n=""+s,a.call(this,s)}}),Object.defineProperty(e,t,{enumerable:r.enumerable}),{getValue:function(){return n},setValue:function(s){n=""+s},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Os(e){e._valueTracker||(e._valueTracker=Hv(e))}function Jh(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=Zh(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function To(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Pu(e,t){var r=t.checked;return st({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function _0(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=Wn(t.value!=null?t.value:r),e._wrapperState={initialChecked:n,initialValue:r,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function ep(e,t){t=t.checked,t!=null&&tf(e,"checked",t,!1)}function Uu(e,t){ep(e,t);var r=Wn(t.value),n=t.type;if(r!=null)n==="number"?(r===0&&e.value===""||e.value!=r)&&(e.value=""+r):e.value!==""+r&&(e.value=""+r);else if(n==="submit"||n==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Mu(e,t.type,r):t.hasOwnProperty("defaultValue")&&Mu(e,t.type,Wn(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function A0(e,t,r){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var n=t.type;if(!(n!=="submit"&&n!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,r||t===e.value||(e.value=t),e.defaultValue=t}r=e.name,r!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,r!==""&&(e.name=r)}function Mu(e,t,r){(t!=="number"||To(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var Pa=Array.isArray;function Yi(e,t,r,n){if(e=e.options,t){t={};for(var i=0;i"+t.valueOf().toString()+"",t=Ps.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function ns(e,t){if(t){var r=e.firstChild;if(r&&r===e.lastChild&&r.nodeType===3){r.nodeValue=t;return}}e.textContent=t}var Ka={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},qv=["Webkit","ms","Moz","O"];Object.keys(Ka).forEach(function(e){qv.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Ka[t]=Ka[e]})});function ip(e,t,r){return t==null||typeof t=="boolean"||t===""?"":r||typeof t!="number"||t===0||Ka.hasOwnProperty(e)&&Ka[e]?(""+t).trim():t+"px"}function ap(e,t){e=e.style;for(var r in t)if(t.hasOwnProperty(r)){var n=r.indexOf("--")===0,i=ip(r,t[r],n);r==="float"&&(r="cssFloat"),n?e.setProperty(r,i):e[r]=i}}var $v=st({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ju(e,t){if(t){if($v[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(H(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(H(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(H(61))}if(t.style!=null&&typeof t.style!="object")throw Error(H(62))}}function Ku(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var zu=null;function sf(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Hu=null,Xi=null,Zi=null;function b0(e){if(e=_s(e)){if(typeof Hu!="function")throw Error(H(280));var t=e.stateNode;t&&(t=ul(t),Hu(e.stateNode,e.type,t))}}function sp(e){Xi?Zi?Zi.push(e):Zi=[e]:Xi=e}function op(){if(Xi){var e=Xi,t=Zi;if(Zi=Xi=null,b0(e),t)for(e=0;e>>=0,e===0?32:31-(ny(e)/iy|0)|0}var Us=64,Ms=4194304;function Ua(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Ao(e,t){var r=e.pendingLanes;if(r===0)return 0;var n=0,i=e.suspendedLanes,a=e.pingedLanes,s=r&268435455;if(s!==0){var o=s&~i;o!==0?n=Ua(o):(a&=s,a!==0&&(n=Ua(a)))}else s=r&~i,s!==0?n=Ua(s):a!==0&&(n=Ua(a));if(n===0)return 0;if(t!==0&&t!==n&&!(t&i)&&(i=n&-n,a=t&-t,i>=a||i===16&&(a&4194240)!==0))return t;if(n&4&&(n|=r&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=n;0r;r++)t.push(e);return t}function ws(e,t,r){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Fr(t),e[t]=r}function ly(e,t){var r=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var n=e.eventTimes;for(e=e.expirationTimes;0=Ha),F0=String.fromCharCode(32),V0=!1;function Ap(e,t){switch(e){case"keyup":return Py.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Bp(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ui=!1;function My(e,t){switch(e){case"compositionend":return Bp(t);case"keypress":return t.which!==32?null:(V0=!0,F0);case"textInput":return e=t.data,e===F0&&V0?null:e;default:return null}}function Fy(e,t){if(Ui)return e==="compositionend"||!pf&&Ap(e,t)?(e=Ip(),oo=ff=Dn=null,Ui=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=H0(r)}}function Dp(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Dp(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Rp(){for(var e=window,t=To();t instanceof e.HTMLIFrameElement;){try{var r=typeof t.contentWindow.location.href=="string"}catch{r=!1}if(r)e=t.contentWindow;else break;t=To(e.document)}return t}function gf(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Qy(e){var t=Rp(),r=e.focusedElem,n=e.selectionRange;if(t!==r&&r&&r.ownerDocument&&Dp(r.ownerDocument.documentElement,r)){if(n!==null&&gf(r)){if(t=n.start,e=n.end,e===void 0&&(e=t),"selectionStart"in r)r.selectionStart=t,r.selectionEnd=Math.min(e,r.value.length);else if(e=(t=r.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var i=r.textContent.length,a=Math.min(n.start,i);n=n.end===void 0?a:Math.min(n.end,i),!e.extend&&a>n&&(i=n,n=a,a=i),i=q0(r,a);var s=q0(r,n);i&&s&&(e.rangeCount!==1||e.anchorNode!==i.node||e.anchorOffset!==i.offset||e.focusNode!==s.node||e.focusOffset!==s.offset)&&(t=t.createRange(),t.setStart(i.node,i.offset),e.removeAllRanges(),a>n?(e.addRange(t),e.extend(s.node,s.offset)):(t.setEnd(s.node,s.offset),e.addRange(t)))}}for(t=[],e=r;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof r.focus=="function"&&r.focus(),r=0;r=document.documentMode,Mi=null,Yu=null,$a=null,Xu=!1;function $0(e,t,r){var n=r.window===r?r.document:r.nodeType===9?r:r.ownerDocument;Xu||Mi==null||Mi!==To(n)||(n=Mi,"selectionStart"in n&&gf(n)?n={start:n.selectionStart,end:n.selectionEnd}:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}),$a&&us($a,n)||($a=n,n=bo(Yu,"onSelect"),0ji||(e.current=nc[ji],nc[ji]=null,ji--)}function Ge(e,t){ji++,nc[ji]=e.current,e.current=t}var Yn={},Mt=ei(Yn),Xt=ei(!1),vi=Yn;function na(e,t){var r=e.type.contextTypes;if(!r)return Yn;var n=e.stateNode;if(n&&n.__reactInternalMemoizedUnmaskedChildContext===t)return n.__reactInternalMemoizedMaskedChildContext;var i={},a;for(a in r)i[a]=t[a];return n&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=i),i}function Zt(e){return e=e.childContextTypes,e!=null}function Do(){et(Xt),et(Mt)}function J0(e,t,r){if(Mt.current!==Yn)throw Error(H(168));Ge(Mt,t),Ge(Xt,r)}function Kp(e,t,r){var n=e.stateNode;if(t=t.childContextTypes,typeof n.getChildContext!="function")return r;n=n.getChildContext();for(var i in n)if(!(i in t))throw Error(H(108,zv(e)||"Unknown",i));return st({},r,n)}function Ro(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Yn,vi=Mt.current,Ge(Mt,e),Ge(Xt,Xt.current),!0}function ed(e,t,r){var n=e.stateNode;if(!n)throw Error(H(169));r?(e=Kp(e,t,vi),n.__reactInternalMemoizedMergedChildContext=e,et(Xt),et(Mt),Ge(Mt,e)):et(Xt),Ge(Xt,r)}var ln=null,cl=!1,ru=!1;function zp(e){ln===null?ln=[e]:ln.push(e)}function sm(e){cl=!0,zp(e)}function ti(){if(!ru&&ln!==null){ru=!0;var e=0,t=Me;try{var r=ln;for(Me=1;e>=s,i-=s,un=1<<32-Fr(t)+i|r<V?(Q=M,M=null):Q=M.sibling;var G=d(y,M,S[V],B);if(G===null){M===null&&(M=Q);break}e&&M&&G.alternate===null&&t(y,M),m=a(G,m,V),L===null?R=G:L.sibling=G,L=G,M=Q}if(V===S.length)return r(y,M),rt&&ni(y,V),R;if(M===null){for(;VV?(Q=M,M=null):Q=M.sibling;var ce=d(y,M,G.value,B);if(ce===null){M===null&&(M=Q);break}e&&M&&ce.alternate===null&&t(y,M),m=a(ce,m,V),L===null?R=ce:L.sibling=ce,L=ce,M=Q}if(G.done)return r(y,M),rt&&ni(y,V),R;if(M===null){for(;!G.done;V++,G=S.next())G=f(y,G.value,B),G!==null&&(m=a(G,m,V),L===null?R=G:L.sibling=G,L=G);return rt&&ni(y,V),R}for(M=n(y,M);!G.done;V++,G=S.next())G=v(M,y,V,G.value,B),G!==null&&(e&&G.alternate!==null&&M.delete(G.key===null?V:G.key),m=a(G,m,V),L===null?R=G:L.sibling=G,L=G);return e&&M.forEach(function(pe){return t(y,pe)}),rt&&ni(y,V),R}function I(y,m,S,B){if(typeof S=="object"&&S!==null&&S.type===Pi&&S.key===null&&(S=S.props.children),typeof S=="object"&&S!==null){switch(S.$$typeof){case Ls:e:{for(var R=S.key,L=m;L!==null;){if(L.key===R){if(R=S.type,R===Pi){if(L.tag===7){r(y,L.sibling),m=i(L,S.props.children),m.return=y,y=m;break e}}else if(L.elementType===R||typeof R=="object"&&R!==null&&R.$$typeof===An&&od(R)===L.type){r(y,L.sibling),m=i(L,S.props),m.ref=Ba(y,L,S),m.return=y,y=m;break e}r(y,L);break}else t(y,L);L=L.sibling}S.type===Pi?(m=hi(S.props.children,y.mode,B,S.key),m.return=y,y=m):(B=vo(S.type,S.key,S.props,null,y.mode,B),B.ref=Ba(y,m,S),B.return=y,y=B)}return s(y);case Oi:e:{for(L=S.key;m!==null;){if(m.key===L)if(m.tag===4&&m.stateNode.containerInfo===S.containerInfo&&m.stateNode.implementation===S.implementation){r(y,m.sibling),m=i(m,S.children||[]),m.return=y,y=m;break e}else{r(y,m);break}else t(y,m);m=m.sibling}m=cu(S,y.mode,B),m.return=y,y=m}return s(y);case An:return L=S._init,I(y,m,L(S._payload),B)}if(Pa(S))return g(y,m,S,B);if(Ta(S))return C(y,m,S,B);qs(y,S)}return typeof S=="string"&&S!==""||typeof S=="number"?(S=""+S,m!==null&&m.tag===6?(r(y,m.sibling),m=i(m,S),m.return=y,y=m):(r(y,m),m=uu(S,y.mode,B),m.return=y,y=m),s(y)):r(y,m)}return I}var aa=Xp(!0),Zp=Xp(!1),As={},tn=ei(As),hs=ei(As),ps=ei(As);function ci(e){if(e===As)throw Error(H(174));return e}function wf(e,t){switch(Ge(ps,t),Ge(hs,e),Ge(tn,As),e=t.nodeType,e){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:Vu(null,"");break;default:e=e===8?t.parentNode:t,t=e.namespaceURI||null,e=e.tagName,t=Vu(t,e)}et(tn),Ge(tn,t)}function sa(){et(tn),et(hs),et(ps)}function Jp(e){ci(ps.current);var t=ci(tn.current),r=Vu(t,e.type);t!==r&&(Ge(hs,e),Ge(tn,r))}function If(e){hs.current===e&&(et(tn),et(hs))}var it=ei(0);function Fo(e){for(var t=e;t!==null;){if(t.tag===13){var r=t.memoizedState;if(r!==null&&(r=r.dehydrated,r===null||r.data==="$?"||r.data==="$!"))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if(t.flags&128)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var nu=[];function _f(){for(var e=0;er?r:4,e(!0);var n=iu.transition;iu.transition={};try{e(!1),t()}finally{Me=r,iu.transition=n}}function gg(){return Nr().memoizedState}function cm(e,t,r){var n=qn(e);if(r={lane:n,action:r,hasEagerState:!1,eagerState:null,next:null},vg(e))yg(t,r);else if(r=Gp(e,t,r,n),r!==null){var i=Ht();Vr(r,e,n,i),mg(r,t,n)}}function fm(e,t,r){var n=qn(e),i={lane:n,action:r,hasEagerState:!1,eagerState:null,next:null};if(vg(e))yg(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var s=t.lastRenderedState,o=a(s,r);if(i.hasEagerState=!0,i.eagerState=o,jr(o,s)){var l=t.interleaved;l===null?(i.next=i,xf(t)):(i.next=l.next,l.next=i),t.interleaved=i;return}}catch{}finally{}r=Gp(e,t,i,n),r!==null&&(i=Ht(),Vr(r,e,n,i),mg(r,t,n))}}function vg(e){var t=e.alternate;return e===at||t!==null&&t===at}function yg(e,t){Ga=Vo=!0;var r=e.pending;r===null?t.next=t:(t.next=r.next,r.next=t),e.pending=t}function mg(e,t,r){if(r&4194240){var n=t.lanes;n&=e.pendingLanes,r|=n,t.lanes=r,lf(e,r)}}var jo={readContext:br,useCallback:Lt,useContext:Lt,useEffect:Lt,useImperativeHandle:Lt,useInsertionEffect:Lt,useLayoutEffect:Lt,useMemo:Lt,useReducer:Lt,useRef:Lt,useState:Lt,useDebugValue:Lt,useDeferredValue:Lt,useTransition:Lt,useMutableSource:Lt,useSyncExternalStore:Lt,useId:Lt,unstable_isNewReconciler:!1},dm={readContext:br,useCallback:function(e,t){return Hr().memoizedState=[e,t===void 0?null:t],e},useContext:br,useEffect:ud,useImperativeHandle:function(e,t,r){return r=r!=null?r.concat([e]):null,fo(4194308,4,cg.bind(null,t,e),r)},useLayoutEffect:function(e,t){return fo(4194308,4,e,t)},useInsertionEffect:function(e,t){return fo(4,2,e,t)},useMemo:function(e,t){var r=Hr();return t=t===void 0?null:t,e=e(),r.memoizedState=[e,t],e},useReducer:function(e,t,r){var n=Hr();return t=r!==void 0?r(t):t,n.memoizedState=n.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},n.queue=e,e=e.dispatch=cm.bind(null,at,e),[n.memoizedState,e]},useRef:function(e){var t=Hr();return e={current:e},t.memoizedState=e},useState:ld,useDebugValue:Nf,useDeferredValue:function(e){return Hr().memoizedState=e},useTransition:function(){var e=ld(!1),t=e[0];return e=um.bind(null,e[1]),Hr().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,r){var n=at,i=Hr();if(rt){if(r===void 0)throw Error(H(407));r=r()}else{if(r=t(),It===null)throw Error(H(349));mi&30||rg(n,t,r)}i.memoizedState=r;var a={value:r,getSnapshot:t};return i.queue=a,ud(ig.bind(null,n,a,e),[e]),n.flags|=2048,ys(9,ng.bind(null,n,a,r,t),void 0,null),r},useId:function(){var e=Hr(),t=It.identifierPrefix;if(rt){var r=cn,n=un;r=(n&~(1<<32-Fr(n)-1)).toString(32)+r,t=":"+t+"R"+r,r=gs++,0<\/script>",e=e.removeChild(e.firstChild)):typeof n.is=="string"?e=s.createElement(r,{is:n.is}):(e=s.createElement(r),r==="select"&&(s=e,n.multiple?s.multiple=!0:n.size&&(s.size=n.size))):e=s.createElementNS(e,r),e[Qr]=t,e[ds]=n,Ag(e,t,!1,!1),t.stateNode=e;e:{switch(s=Ku(r,n),r){case"dialog":Xe("cancel",e),Xe("close",e),i=n;break;case"iframe":case"object":case"embed":Xe("load",e),i=n;break;case"video":case"audio":for(i=0;ila&&(t.flags|=128,n=!0,ka(a,!1),t.lanes=4194304)}else{if(!n)if(e=Fo(s),e!==null){if(t.flags|=128,n=!0,r=e.updateQueue,r!==null&&(t.updateQueue=r,t.flags|=4),ka(a,!0),a.tail===null&&a.tailMode==="hidden"&&!s.alternate&&!rt)return Ot(t),null}else 2*dt()-a.renderingStartTime>la&&r!==1073741824&&(t.flags|=128,n=!0,ka(a,!1),t.lanes=4194304);a.isBackwards?(s.sibling=t.child,t.child=s):(r=a.last,r!==null?r.sibling=s:t.child=s,a.last=s)}return a.tail!==null?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=dt(),t.sibling=null,r=it.current,Ge(it,n?r&1|2:r&1),t):(Ot(t),null);case 22:case 23:return Uf(),n=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==n&&(t.flags|=8192),n&&t.mode&1?ar&1073741824&&(Ot(t),t.subtreeFlags&6&&(t.flags|=8192)):Ot(t),null;case 24:return null;case 25:return null}throw Error(H(156,t.tag))}function Em(e,t){switch(yf(t),t.tag){case 1:return Zt(t.type)&&Do(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return sa(),et(Xt),et(Mt),_f(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return If(t),null;case 13:if(et(it),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(H(340));ia()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return et(it),null;case 4:return sa(),null;case 10:return Sf(t.type._context),null;case 22:case 23:return Uf(),null;case 24:return null;default:return null}}var Gs=!1,Ut=!1,Sm=typeof WeakSet=="function"?WeakSet:Set,re=null;function qi(e,t){var r=e.ref;if(r!==null)if(typeof r=="function")try{r(null)}catch(n){lt(e,t,n)}else r.current=null}function gc(e,t,r){try{r()}catch(n){lt(e,t,n)}}var md=!1;function xm(e,t){if(Zu=Bo,e=Rp(),gf(e)){if("selectionStart"in e)var r={start:e.selectionStart,end:e.selectionEnd};else e:{r=(r=e.ownerDocument)&&r.defaultView||window;var n=r.getSelection&&r.getSelection();if(n&&n.rangeCount!==0){r=n.anchorNode;var i=n.anchorOffset,a=n.focusNode;n=n.focusOffset;try{r.nodeType,a.nodeType}catch{r=null;break e}var s=0,o=-1,l=-1,u=0,c=0,f=e,d=null;t:for(;;){for(var v;f!==r||i!==0&&f.nodeType!==3||(o=s+i),f!==a||n!==0&&f.nodeType!==3||(l=s+n),f.nodeType===3&&(s+=f.nodeValue.length),(v=f.firstChild)!==null;)d=f,f=v;for(;;){if(f===e)break t;if(d===r&&++u===i&&(o=s),d===a&&++c===n&&(l=s),(v=f.nextSibling)!==null)break;f=d,d=f.parentNode}f=v}r=o===-1||l===-1?null:{start:o,end:l}}else r=null}r=r||{start:0,end:0}}else r=null;for(Ju={focusedElem:e,selectionRange:r},Bo=!1,re=t;re!==null;)if(t=re,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,re=e;else for(;re!==null;){t=re;try{var g=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(g!==null){var C=g.memoizedProps,I=g.memoizedState,y=t.stateNode,m=y.getSnapshotBeforeUpdate(t.elementType===t.type?C:Or(t.type,C),I);y.__reactInternalSnapshotBeforeUpdate=m}break;case 3:var S=t.stateNode.containerInfo;S.nodeType===1?S.textContent="":S.nodeType===9&&S.documentElement&&S.removeChild(S.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(H(163))}}catch(B){lt(t,t.return,B)}if(e=t.sibling,e!==null){e.return=t.return,re=e;break}re=t.return}return g=md,md=!1,g}function Qa(e,t,r){var n=t.updateQueue;if(n=n!==null?n.lastEffect:null,n!==null){var i=n=n.next;do{if((i.tag&e)===e){var a=i.destroy;i.destroy=void 0,a!==void 0&&gc(t,r,a)}i=i.next}while(i!==n)}}function hl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var r=t=t.next;do{if((r.tag&e)===e){var n=r.create;r.destroy=n()}r=r.next}while(r!==t)}}function vc(e){var t=e.ref;if(t!==null){var r=e.stateNode;switch(e.tag){case 5:e=r;break;default:e=r}typeof t=="function"?t(e):t.current=e}}function bg(e){var t=e.alternate;t!==null&&(e.alternate=null,bg(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Qr],delete t[ds],delete t[rc],delete t[im],delete t[am])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Ng(e){return e.tag===5||e.tag===3||e.tag===4}function Cd(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Ng(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function yc(e,t,r){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?r.nodeType===8?r.parentNode.insertBefore(e,t):r.insertBefore(e,t):(r.nodeType===8?(t=r.parentNode,t.insertBefore(e,r)):(t=r,t.appendChild(e)),r=r._reactRootContainer,r!=null||t.onclick!==null||(t.onclick=No));else if(n!==4&&(e=e.child,e!==null))for(yc(e,t,r),e=e.sibling;e!==null;)yc(e,t,r),e=e.sibling}function mc(e,t,r){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?r.insertBefore(e,t):r.appendChild(e);else if(n!==4&&(e=e.child,e!==null))for(mc(e,t,r),e=e.sibling;e!==null;)mc(e,t,r),e=e.sibling}var Bt=null,Pr=!1;function xn(e,t,r){for(r=r.child;r!==null;)Dg(e,t,r),r=r.sibling}function Dg(e,t,r){if(en&&typeof en.onCommitFiberUnmount=="function")try{en.onCommitFiberUnmount(al,r)}catch{}switch(r.tag){case 5:Ut||qi(r,t);case 6:var n=Bt,i=Pr;Bt=null,xn(e,t,r),Bt=n,Pr=i,Bt!==null&&(Pr?(e=Bt,r=r.stateNode,e.nodeType===8?e.parentNode.removeChild(r):e.removeChild(r)):Bt.removeChild(r.stateNode));break;case 18:Bt!==null&&(Pr?(e=Bt,r=r.stateNode,e.nodeType===8?tu(e.parentNode,r):e.nodeType===1&&tu(e,r),os(e)):tu(Bt,r.stateNode));break;case 4:n=Bt,i=Pr,Bt=r.stateNode.containerInfo,Pr=!0,xn(e,t,r),Bt=n,Pr=i;break;case 0:case 11:case 14:case 15:if(!Ut&&(n=r.updateQueue,n!==null&&(n=n.lastEffect,n!==null))){i=n=n.next;do{var a=i,s=a.destroy;a=a.tag,s!==void 0&&(a&2||a&4)&&gc(r,t,s),i=i.next}while(i!==n)}xn(e,t,r);break;case 1:if(!Ut&&(qi(r,t),n=r.stateNode,typeof n.componentWillUnmount=="function"))try{n.props=r.memoizedProps,n.state=r.memoizedState,n.componentWillUnmount()}catch(o){lt(r,t,o)}xn(e,t,r);break;case 21:xn(e,t,r);break;case 22:r.mode&1?(Ut=(n=Ut)||r.memoizedState!==null,xn(e,t,r),Ut=n):xn(e,t,r);break;default:xn(e,t,r)}}function Ed(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var r=e.stateNode;r===null&&(r=e.stateNode=new Sm),t.forEach(function(n){var i=Nm.bind(null,e,n);r.has(n)||(r.add(n),n.then(i,i))})}}function Rr(e,t){var r=t.deletions;if(r!==null)for(var n=0;ni&&(i=s),n&=~a}if(n=i,n=dt()-n,n=(120>n?120:480>n?480:1080>n?1080:1920>n?1920:3e3>n?3e3:4320>n?4320:1960*wm(n/1960))-n,10e?16:e,Rn===null)var n=!1;else{if(e=Rn,Rn=null,Ho=0,Ae&6)throw Error(H(331));var i=Ae;for(Ae|=4,re=e.current;re!==null;){var a=re,s=a.child;if(re.flags&16){var o=a.deletions;if(o!==null){for(var l=0;ldt()-Of?di(e,0):Lf|=r),Jt(e,t)}function Vg(e,t){t===0&&(e.mode&1?(t=Ms,Ms<<=1,!(Ms&130023424)&&(Ms=4194304)):t=1);var r=Ht();e=vn(e,t),e!==null&&(ws(e,t,r),Jt(e,r))}function bm(e){var t=e.memoizedState,r=0;t!==null&&(r=t.retryLane),Vg(e,r)}function Nm(e,t){var r=0;switch(e.tag){case 13:var n=e.stateNode,i=e.memoizedState;i!==null&&(r=i.retryLane);break;case 19:n=e.stateNode;break;default:throw Error(H(314))}n!==null&&n.delete(t),Vg(e,r)}var jg;jg=function(e,t,r){if(e!==null)if(e.memoizedProps!==t.pendingProps||Xt.current)Wt=!0;else{if(!(e.lanes&r)&&!(t.flags&128))return Wt=!1,mm(e,t,r);Wt=!!(e.flags&131072)}else Wt=!1,rt&&t.flags&1048576&&Hp(t,Oo,t.index);switch(t.lanes=0,t.tag){case 2:var n=t.type;ho(e,t),e=t.pendingProps;var i=na(t,Mt.current);ea(t,r),i=Bf(null,t,n,e,i,r);var a=kf();return t.flags|=1,typeof i=="object"&&i!==null&&typeof i.render=="function"&&i.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Zt(n)?(a=!0,Ro(t)):a=!1,t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,Tf(t),i.updater=fl,t.stateNode=i,i._reactInternals=t,lc(t,n,e,r),t=fc(null,t,n,!0,a,r)):(t.tag=0,rt&&a&&vf(t),jt(null,t,i,r),t=t.child),t;case 16:n=t.elementType;e:{switch(ho(e,t),e=t.pendingProps,i=n._init,n=i(n._payload),t.type=n,i=t.tag=Rm(n),e=Or(n,e),i){case 0:t=cc(null,t,n,e,r);break e;case 1:t=gd(null,t,n,e,r);break e;case 11:t=hd(null,t,n,e,r);break e;case 14:t=pd(null,t,n,Or(n.type,e),r);break e}throw Error(H(306,n,""))}return t;case 0:return n=t.type,i=t.pendingProps,i=t.elementType===n?i:Or(n,i),cc(e,t,n,i,r);case 1:return n=t.type,i=t.pendingProps,i=t.elementType===n?i:Or(n,i),gd(e,t,n,i,r);case 3:e:{if(wg(t),e===null)throw Error(H(387));n=t.pendingProps,a=t.memoizedState,i=a.element,Qp(e,t),Mo(t,n,null,r);var s=t.memoizedState;if(n=s.element,a.isDehydrated)if(a={element:n,isDehydrated:!1,cache:s.cache,pendingSuspenseBoundaries:s.pendingSuspenseBoundaries,transitions:s.transitions},t.updateQueue.baseState=a,t.memoizedState=a,t.flags&256){i=oa(Error(H(423)),t),t=vd(e,t,n,r,i);break e}else if(n!==i){i=oa(Error(H(424)),t),t=vd(e,t,n,r,i);break e}else for(lr=Kn(t.stateNode.containerInfo.firstChild),ur=t,rt=!0,Ur=null,r=Zp(t,null,n,r),t.child=r;r;)r.flags=r.flags&-3|4096,r=r.sibling;else{if(ia(),n===i){t=yn(e,t,r);break e}jt(e,t,n,r)}t=t.child}return t;case 5:return Jp(t),e===null&&ac(t),n=t.type,i=t.pendingProps,a=e!==null?e.memoizedProps:null,s=i.children,ec(n,i)?s=null:a!==null&&ec(n,a)&&(t.flags|=32),Tg(e,t),jt(e,t,s,r),t.child;case 6:return e===null&&ac(t),null;case 13:return Ig(e,t,r);case 4:return wf(t,t.stateNode.containerInfo),n=t.pendingProps,e===null?t.child=aa(t,null,n,r):jt(e,t,n,r),t.child;case 11:return n=t.type,i=t.pendingProps,i=t.elementType===n?i:Or(n,i),hd(e,t,n,i,r);case 7:return jt(e,t,t.pendingProps,r),t.child;case 8:return jt(e,t,t.pendingProps.children,r),t.child;case 12:return jt(e,t,t.pendingProps.children,r),t.child;case 10:e:{if(n=t.type._context,i=t.pendingProps,a=t.memoizedProps,s=i.value,Ge(Po,n._currentValue),n._currentValue=s,a!==null)if(jr(a.value,s)){if(a.children===i.children&&!Xt.current){t=yn(e,t,r);break e}}else for(a=t.child,a!==null&&(a.return=t);a!==null;){var o=a.dependencies;if(o!==null){s=a.child;for(var l=o.firstContext;l!==null;){if(l.context===n){if(a.tag===1){l=dn(-1,r&-r),l.tag=2;var u=a.updateQueue;if(u!==null){u=u.shared;var c=u.pending;c===null?l.next=l:(l.next=c.next,c.next=l),u.pending=l}}a.lanes|=r,l=a.alternate,l!==null&&(l.lanes|=r),sc(a.return,r,t),o.lanes|=r;break}l=l.next}}else if(a.tag===10)s=a.type===t.type?null:a.child;else if(a.tag===18){if(s=a.return,s===null)throw Error(H(341));s.lanes|=r,o=s.alternate,o!==null&&(o.lanes|=r),sc(s,r,t),s=a.sibling}else s=a.child;if(s!==null)s.return=a;else for(s=a;s!==null;){if(s===t){s=null;break}if(a=s.sibling,a!==null){a.return=s.return,s=a;break}s=s.return}a=s}jt(e,t,i.children,r),t=t.child}return t;case 9:return i=t.type,n=t.pendingProps.children,ea(t,r),i=br(i),n=n(i),t.flags|=1,jt(e,t,n,r),t.child;case 14:return n=t.type,i=Or(n,t.pendingProps),i=Or(n.type,i),pd(e,t,n,i,r);case 15:return Sg(e,t,t.type,t.pendingProps,r);case 17:return n=t.type,i=t.pendingProps,i=t.elementType===n?i:Or(n,i),ho(e,t),t.tag=1,Zt(n)?(e=!0,Ro(t)):e=!1,ea(t,r),Yp(t,n,i),lc(t,n,i,r),fc(null,t,n,!0,e,r);case 19:return _g(e,t,r);case 22:return xg(e,t,r)}throw Error(H(156,t.tag))};function Kg(e,t){return pp(e,t)}function Dm(e,t,r,n){this.tag=e,this.key=r,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=n,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function wr(e,t,r,n){return new Dm(e,t,r,n)}function Ff(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Rm(e){if(typeof e=="function")return Ff(e)?1:0;if(e!=null){if(e=e.$$typeof,e===nf)return 11;if(e===af)return 14}return 2}function $n(e,t){var r=e.alternate;return r===null?(r=wr(e.tag,t,e.key,e.mode),r.elementType=e.elementType,r.type=e.type,r.stateNode=e.stateNode,r.alternate=e,e.alternate=r):(r.pendingProps=t,r.type=e.type,r.flags=0,r.subtreeFlags=0,r.deletions=null),r.flags=e.flags&14680064,r.childLanes=e.childLanes,r.lanes=e.lanes,r.child=e.child,r.memoizedProps=e.memoizedProps,r.memoizedState=e.memoizedState,r.updateQueue=e.updateQueue,t=e.dependencies,r.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},r.sibling=e.sibling,r.index=e.index,r.ref=e.ref,r}function vo(e,t,r,n,i,a){var s=2;if(n=e,typeof e=="function")Ff(e)&&(s=1);else if(typeof e=="string")s=5;else e:switch(e){case Pi:return hi(r.children,i,a,t);case rf:s=8,i|=8;break;case Du:return e=wr(12,r,t,i|2),e.elementType=Du,e.lanes=a,e;case Ru:return e=wr(13,r,t,i),e.elementType=Ru,e.lanes=a,e;case Lu:return e=wr(19,r,t,i),e.elementType=Lu,e.lanes=a,e;case Xh:return gl(r,i,a,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Wh:s=10;break e;case Yh:s=9;break e;case nf:s=11;break e;case af:s=14;break e;case An:s=16,n=null;break e}throw Error(H(130,e==null?e:typeof e,""))}return t=wr(s,r,t,i),t.elementType=e,t.type=n,t.lanes=a,t}function hi(e,t,r,n){return e=wr(7,e,n,t),e.lanes=r,e}function gl(e,t,r,n){return e=wr(22,e,n,t),e.elementType=Xh,e.lanes=r,e.stateNode={isHidden:!1},e}function uu(e,t,r){return e=wr(6,e,null,t),e.lanes=r,e}function cu(e,t,r){return t=wr(4,e.children!==null?e.children:[],e.key,t),t.lanes=r,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Lm(e,t,r,n,i){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Hl(0),this.expirationTimes=Hl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Hl(0),this.identifierPrefix=n,this.onRecoverableError=i,this.mutableSourceEagerHydrationData=null}function Vf(e,t,r,n,i,a,s,o,l){return e=new Lm(e,t,r,o,l),t===1?(t=1,a===!0&&(t|=8)):t=0,a=wr(3,null,null,t),e.current=a,a.stateNode=e,a.memoizedState={element:n,isDehydrated:r,cache:null,transitions:null,pendingSuspenseBoundaries:null},Tf(a),e}function Om(e,t,r){var n=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE($g)}catch(e){console.error(e)}}$g(),Hh.exports=fr;var Vm=Hh.exports,Bd=Vm;bu.createRoot=Bd.createRoot,bu.hydrateRoot=Bd.hydrateRoot;/** - * @license - * Copyright (c) 2023, Jeff Hlywa (jhlywa@gmail.com) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */const Pt="w",mr="b",Et="p",Tc="n",yo="b",Fa="r",kn="q",St="k",fu="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",nr=-1,jm={NORMAL:"n",CAPTURE:"c",BIG_PAWN:"b",EP_CAPTURE:"e",PROMOTION:"p",KSIDE_CASTLE:"k",QSIDE_CASTLE:"q"},fe={NORMAL:1,CAPTURE:2,BIG_PAWN:4,EP_CAPTURE:8,PROMOTION:16,KSIDE_CASTLE:32,QSIDE_CASTLE:64},ve={a8:0,b8:1,c8:2,d8:3,e8:4,f8:5,g8:6,h8:7,a7:16,b7:17,c7:18,d7:19,e7:20,f7:21,g7:22,h7:23,a6:32,b6:33,c6:34,d6:35,e6:36,f6:37,g6:38,h6:39,a5:48,b5:49,c5:50,d5:51,e5:52,f5:53,g5:54,h5:55,a4:64,b4:65,c4:66,d4:67,e4:68,f4:69,g4:70,h4:71,a3:80,b3:81,c3:82,d3:83,e3:84,f3:85,g3:86,h3:87,a2:96,b2:97,c2:98,d2:99,e2:100,f2:101,g2:102,h2:103,a1:112,b1:113,c1:114,d1:115,e1:116,f1:117,g1:118,h1:119},du={b:[16,32,17,15],w:[-16,-32,-17,-15]},kd={n:[-18,-33,-31,-14,18,33,31,14],b:[-17,-15,17,15],r:[-16,1,16,-1],q:[-17,-16,-15,1,17,16,15,-1],k:[-17,-16,-15,1,17,16,15,-1]},Km=[20,0,0,0,0,0,0,24,0,0,0,0,0,0,20,0,0,20,0,0,0,0,0,24,0,0,0,0,0,20,0,0,0,0,20,0,0,0,0,24,0,0,0,0,20,0,0,0,0,0,0,20,0,0,0,24,0,0,0,20,0,0,0,0,0,0,0,0,20,0,0,24,0,0,20,0,0,0,0,0,0,0,0,0,0,20,2,24,2,20,0,0,0,0,0,0,0,0,0,0,0,2,53,56,53,2,0,0,0,0,0,0,24,24,24,24,24,24,56,0,56,24,24,24,24,24,24,0,0,0,0,0,0,2,53,56,53,2,0,0,0,0,0,0,0,0,0,0,0,20,2,24,2,20,0,0,0,0,0,0,0,0,0,0,20,0,0,24,0,0,20,0,0,0,0,0,0,0,0,20,0,0,0,24,0,0,0,20,0,0,0,0,0,0,20,0,0,0,0,24,0,0,0,0,20,0,0,0,0,20,0,0,0,0,0,24,0,0,0,0,0,20,0,0,20,0,0,0,0,0,0,24,0,0,0,0,0,0,20],zm=[17,0,0,0,0,0,0,16,0,0,0,0,0,0,15,0,0,17,0,0,0,0,0,16,0,0,0,0,0,15,0,0,0,0,17,0,0,0,0,16,0,0,0,0,15,0,0,0,0,0,0,17,0,0,0,16,0,0,0,15,0,0,0,0,0,0,0,0,17,0,0,16,0,0,15,0,0,0,0,0,0,0,0,0,0,17,0,16,0,15,0,0,0,0,0,0,0,0,0,0,0,0,17,16,15,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,-15,-16,-17,0,0,0,0,0,0,0,0,0,0,0,0,-15,0,-16,0,-17,0,0,0,0,0,0,0,0,0,0,-15,0,0,-16,0,0,-17,0,0,0,0,0,0,0,0,-15,0,0,0,-16,0,0,0,-17,0,0,0,0,0,0,-15,0,0,0,0,-16,0,0,0,0,-17,0,0,0,0,-15,0,0,0,0,0,-16,0,0,0,0,0,-17,0,0,-15,0,0,0,0,0,0,-16,0,0,0,0,0,0,-17],Hm={p:1,n:2,b:4,r:8,q:16,k:32},qm="pnbrqkPNBRQK",bd=[Tc,yo,Fa,kn],$m=7,Gm=6,Qm=1,Wm=0,Ys={[St]:fe.KSIDE_CASTLE,[kn]:fe.QSIDE_CASTLE},Tn={w:[{square:ve.a1,flag:fe.QSIDE_CASTLE},{square:ve.h1,flag:fe.KSIDE_CASTLE}],b:[{square:ve.a8,flag:fe.QSIDE_CASTLE},{square:ve.h8,flag:fe.KSIDE_CASTLE}]},Ym={b:Qm,w:Gm},Xm=["1-0","0-1","1/2-1/2","*"];function pi(e){return e>>4}function Cs(e){return e&15}function Gg(e){return"0123456789".indexOf(e)!==-1}function ir(e){const t=Cs(e),r=pi(e);return"abcdefgh".substring(t,t+1)+"87654321".substring(r,r+1)}function Na(e){return e===Pt?mr:Pt}function Zm(e){const t=e.split(/\s+/);if(t.length!==6)return{ok:!1,error:"Invalid FEN: must contain six space-delimited fields"};const r=parseInt(t[5],10);if(isNaN(r)||r<=0)return{ok:!1,error:"Invalid FEN: move number must be a positive integer"};const n=parseInt(t[4],10);if(isNaN(n)||n<0)return{ok:!1,error:"Invalid FEN: half move counter number must be a non-negative integer"};if(!/^(-|[abcdefgh][36])$/.test(t[3]))return{ok:!1,error:"Invalid FEN: en-passant square is invalid"};if(/[^kKqQ-]/.test(t[2]))return{ok:!1,error:"Invalid FEN: castling availability is invalid"};if(!/^(w|b)$/.test(t[1]))return{ok:!1,error:"Invalid FEN: side-to-move is invalid"};const i=t[0].split("/");if(i.length!==8)return{ok:!1,error:"Invalid FEN: piece data does not contain 8 '/'-delimited rows"};for(let s=0;s1)return{ok:!1,error:`Invalid FEN: too many ${s} kings`}}return{ok:!0}}function Jm(e,t){const r=e.from,n=e.to,i=e.piece;let a=0,s=0,o=0;for(let l=0,u=t.length;l0?s>0&&o>0?ir(r):o>0?ir(r).charAt(1):ir(r).charAt(0):""}function wn(e,t,r,n,i,a=void 0,s=fe.NORMAL){const o=pi(n);if(i===Et&&(o===$m||o===Wm))for(let l=0;l="a"&&t<="h"?e.match(/[a-h]\d.*[a-h]\d/)?void 0:Et:(t=t.toLowerCase(),t==="o"?St:t)}function hu(e){return e.replace(/=/,"").replace(/[+#]?[?!]*$/,"")}class e2{constructor(t=fu){ft(this,"_board",new Array(128));ft(this,"_turn",Pt);ft(this,"_header",{});ft(this,"_kings",{w:nr,b:nr});ft(this,"_epSquare",-1);ft(this,"_halfMoves",0);ft(this,"_moveNumber",0);ft(this,"_history",[]);ft(this,"_comments",{});ft(this,"_castling",{w:0,b:0});this.load(t)}clear(t=!1){this._board=new Array(128),this._kings={w:nr,b:nr},this._turn=Pt,this._castling={w:0,b:0},this._epSquare=nr,this._halfMoves=0,this._moveNumber=1,this._history=[],this._comments={},this._header=t?this._header:{},this._updateSetup(this.fen())}removeHeader(t){t in this._header&&delete this._header[t]}load(t,r=!1){let n=t.split(/\s+/);if(n.length>=2&&n.length<6){const l=["-","-","0","1"];t=n.concat(l.slice(-(6-n.length))).join(" ")}n=t.split(/\s+/);const{ok:i,error:a}=Zm(t);if(!i)throw new Error(a);const s=n[0];let o=0;this.clear(r);for(let l=0;l-1&&(this._castling.w|=fe.KSIDE_CASTLE),n[2].indexOf("Q")>-1&&(this._castling.w|=fe.QSIDE_CASTLE),n[2].indexOf("k")>-1&&(this._castling.b|=fe.KSIDE_CASTLE),n[2].indexOf("q")>-1&&(this._castling.b|=fe.QSIDE_CASTLE),this._epSquare=n[3]==="-"?nr:ve[n[3]],this._halfMoves=parseInt(n[4],10),this._moveNumber=parseInt(n[5],10),this._updateSetup(this.fen())}fen(){var a,s;let t=0,r="";for(let o=ve.a8;o<=ve.h1;o++){if(this._board[o]){t>0&&(r+=t,t=0);const{color:l,type:u}=this._board[o];r+=l===Pt?u.toUpperCase():u.toLowerCase()}else t++;o+1&136&&(t>0&&(r+=t),o!==ve.h1&&(r+="/"),t=0,o+=8)}let n="";this._castling[Pt]&fe.KSIDE_CASTLE&&(n+="K"),this._castling[Pt]&fe.QSIDE_CASTLE&&(n+="Q"),this._castling[mr]&fe.KSIDE_CASTLE&&(n+="k"),this._castling[mr]&fe.QSIDE_CASTLE&&(n+="q"),n=n||"-";let i="-";if(this._epSquare!==nr){const o=this._epSquare+(this._turn===Pt?16:-16),l=[o+1,o-1];for(const u of l){if(u&136)continue;const c=this._turn;if(((a=this._board[u])==null?void 0:a.color)===c&&((s=this._board[u])==null?void 0:s.type)===Et){this._makeMove({color:c,from:u,to:this._epSquare,piece:Et,captured:Et,flags:fe.EP_CAPTURE});const f=!this._isKingAttacked(c);if(this._undoMove(),f){i=ir(this._epSquare);break}}}}return[r,this._turn,n,i,this._halfMoves,this._moveNumber].join(" ")}_updateSetup(t){this._history.length>0||(t!==fu?(this._header.SetUp="1",this._header.FEN=t):(delete this._header.SetUp,delete this._header.FEN))}reset(){this.load(fu)}get(t){return this._board[ve[t]]||!1}put({type:t,color:r},n){if(qm.indexOf(t.toLowerCase())===-1||!(n in ve))return!1;const i=ve[n];return t==St&&!(this._kings[r]==nr||this._kings[r]==i)?!1:(this._board[i]={type:t,color:r},t===St&&(this._kings[r]=i),this._updateCastlingRights(),this._updateEnPassantSquare(),this._updateSetup(this.fen()),!0)}remove(t){const r=this.get(t);return delete this._board[ve[t]],r&&r.type===St&&(this._kings[r.color]=nr),this._updateCastlingRights(),this._updateEnPassantSquare(),this._updateSetup(this.fen()),r}_updateCastlingRights(){var n,i,a,s,o,l,u,c,f,d,v,g;const t=((n=this._board[ve.e1])==null?void 0:n.type)===St&&((i=this._board[ve.e1])==null?void 0:i.color)===Pt,r=((a=this._board[ve.e8])==null?void 0:a.type)===St&&((s=this._board[ve.e8])==null?void 0:s.color)===mr;(!t||((o=this._board[ve.a1])==null?void 0:o.type)!==Fa||((l=this._board[ve.a1])==null?void 0:l.color)!==Pt)&&(this._castling.w&=~fe.QSIDE_CASTLE),(!t||((u=this._board[ve.h1])==null?void 0:u.type)!==Fa||((c=this._board[ve.h1])==null?void 0:c.color)!==Pt)&&(this._castling.w&=~fe.KSIDE_CASTLE),(!r||((f=this._board[ve.a8])==null?void 0:f.type)!==Fa||((d=this._board[ve.a8])==null?void 0:d.color)!==mr)&&(this._castling.b&=~fe.QSIDE_CASTLE),(!r||((v=this._board[ve.h8])==null?void 0:v.type)!==Fa||((g=this._board[ve.h8])==null?void 0:g.color)!==mr)&&(this._castling.b&=~fe.KSIDE_CASTLE)}_updateEnPassantSquare(){var a,s;if(this._epSquare===nr)return;const t=this._epSquare+(this._turn===Pt?-16:16),r=this._epSquare+(this._turn===Pt?16:-16),n=[r+1,r-1];if(this._board[t]!==null||this._board[this._epSquare]!==null||((a=this._board[r])==null?void 0:a.color)!==Na(this._turn)||((s=this._board[r])==null?void 0:s.type)!==Et){this._epSquare=nr;return}const i=o=>{var l,u;return!(o&136)&&((l=this._board[o])==null?void 0:l.color)===this._turn&&((u=this._board[o])==null?void 0:u.type)===Et};n.some(i)||(this._epSquare=nr)}_attacked(t,r){for(let n=ve.a8;n<=ve.h1;n++){if(n&136){n+=7;continue}if(this._board[n]===void 0||this._board[n].color!==t)continue;const i=this._board[n],a=n-r;if(a===0)continue;const s=a+119;if(Km[s]&Hm[i.type]){if(i.type===Et){if(a>0){if(i.color===Pt)return!0}else if(i.color===mr)return!0;continue}if(i.type==="n"||i.type==="k")return!0;const o=zm[s];let l=n+o,u=!1;for(;l!==r;){if(this._board[l]!=null){u=!0;break}l+=o}if(!u)return!0}}return!1}_isKingAttacked(t){const r=this._kings[t];return r===-1?!1:this._attacked(Na(t),r)}isAttacked(t,r){return this._attacked(r,ve[t])}isCheck(){return this._isKingAttacked(this._turn)}inCheck(){return this.isCheck()}isCheckmate(){return this.isCheck()&&this._moves().length===0}isStalemate(){return!this.isCheck()&&this._moves().length===0}isInsufficientMaterial(){const t={b:0,n:0,r:0,q:0,k:0,p:0},r=[];let n=0,i=0;for(let a=ve.a8;a<=ve.h1;a++){if(i=(i+1)%2,a&136){a+=7;continue}const s=this._board[a];s&&(t[s.type]=s.type in t?t[s.type]+1:1,s.type===yo&&r.push(i),n++)}if(n===2)return!0;if(n===3&&(t[yo]===1||t[Tc]===1))return!0;if(n===t[yo]+2){let a=0;const s=r.length;for(let o=0;o=3&&(n=!0);const a=t.pop();if(a)this._makeMove(a);else break}return n}isDraw(){return this._halfMoves>=100||this.isStalemate()||this.isInsufficientMaterial()||this.isThreefoldRepetition()}isGameOver(){return this.isCheckmate()||this.isStalemate()||this.isDraw()}moves({verbose:t=!1,square:r=void 0,piece:n=void 0}={}){const i=this._moves({square:r,piece:n});return t?i.map(a=>this._makePretty(a)):i.map(a=>this._moveToSan(a,i))}_moves({legal:t=!0,piece:r=void 0,square:n=void 0}={}){var v;const i=n?n.toLowerCase():void 0,a=r==null?void 0:r.toLowerCase(),s=[],o=this._turn,l=Na(o);let u=ve.a8,c=ve.h1,f=!1;if(i)if(i in ve)u=c=ve[i],f=!0;else return[];for(let g=u;g<=c;g++){if(g&136){g+=7;continue}if(!this._board[g]||this._board[g].color===l)continue;const{type:C}=this._board[g];let I;if(C===Et){if(a&&a!==C)continue;I=g+du[o][0],this._board[I]||(wn(s,o,g,I,Et),I=g+du[o][1],Ym[o]===pi(g)&&!this._board[I]&&wn(s,o,g,I,Et,void 0,fe.BIG_PAWN));for(let y=2;y<4;y++)I=g+du[o][y],!(I&136)&&(((v=this._board[I])==null?void 0:v.color)===l?wn(s,o,g,I,Et,this._board[I].type,fe.CAPTURE):I===this._epSquare&&wn(s,o,g,I,Et,Et,fe.EP_CAPTURE))}else{if(a&&a!==C)continue;for(let y=0,m=kd[C].length;y{const v=this._comments[this.fen()];if(typeof v<"u"){const g=d.length>0?" ":"";d=`${d}${g}{${v}}`}return d},s=[];for(;this._history.length>0;)s.push(this._undoMove());const o=[];let l="";for(s.length===0&&o.push(a(""));s.length>0;){l=a(l);const d=s.pop();if(!d)break;if(!this._history.length&&d.color==="b"){const v=`${this._moveNumber}. ...`;l=l?`${l} ${v}`:v}else d.color==="w"&&(l.length&&o.push(l),l=this._moveNumber+".");l=l+" "+this._moveToSan(d,this._moves({legal:!0})),this._makeMove(d)}if(l.length&&o.push(a(l)),typeof this._header.Result<"u"&&o.push(this._header.Result),r===0)return n.join("")+o.join(" ");const u=function(){return n.length>0&&n[n.length-1]===" "?(n.pop(),!0):!1},c=function(d,v){for(const g of v.split(" "))if(g){if(d+g.length>r){for(;u();)d--;n.push(t),d=0}n.push(g),d+=g.length,n.push(" "),d++}return u()&&d--,d};let f=0;for(let d=0;dr&&o[d].includes("{")){f=c(f,o[d]);continue}f+o[d].length>r&&d!==0?(n[n.length-1]===" "&&n.pop(),n.push(t),f=0):d!==0&&(n.push(" "),f++),n.push(o[d]),f+=o[d].length}return n.join("")}header(...t){for(let r=0;r0&&(B[L]=M)}return B}t=t.trim();const o=new RegExp("^(\\[((?:"+i(n)+")|.)*\\])((?:\\s*"+i(n)+"){2}|(?:\\s*"+i(n)+")*$)").exec(t),l=o&&o.length>=2?o[1]:"";this.reset();const u=a(l);let c="";for(const S in u)S.toLowerCase()==="fen"&&(c=u[S]),this.header(S,u[S]);if(!r)c&&this.load(c,!0);else if(u.SetUp==="1"){if(!("FEN"in u))throw new Error("Invalid PGN: FEN tag must be supplied with SetUp tag");this.load(u.FEN,!0)}function f(S){return Array.from(S).map(function(B){return B.charCodeAt(0)<128?B.charCodeAt(0).toString(16):encodeURIComponent(B).replace(/%/g,"").toLowerCase()}).join("")}function d(S){return S.length==0?"":decodeURIComponent("%"+(S.match(/.{1,2}/g)||[]).join("%"))}const v=function(S){return S=S.replace(new RegExp(i(n),"g")," "),`{${f(S.slice(1,S.length-1))}}`},g=function(S){if(S.startsWith("{")&&S.endsWith("}"))return d(S.slice(1,S.length-1))};let C=t.replace(l,"").replace(new RegExp(`({[^}]*})+?|;([^${i(n)}]*)`,"g"),function(S,B,R){return B!==void 0?v(B):" "+v(`{${R.slice(1)}}`)}).replace(new RegExp(i(n),"g")," ");const I=/(\([^()]+\))+?/g;for(;I.test(C);)C=C.replace(I,"");C=C.replace(/\d+\.(\.\.)?/g,""),C=C.replace(/\.\.\./g,""),C=C.replace(/\$\d+/g,"");let y=C.trim().split(new RegExp(/\s+/));y=y.filter(S=>S!=="");let m="";for(let S=0;S-1)m=y[S];else throw new Error(`Invalid move in PGN: ${y[S]}`);else m="",this._makeMove(R)}m&&Object.keys(this._header).length&&!this._header.Result&&this.header("Result",m)}_moveToSan(t,r){let n="";if(t.flags&fe.KSIDE_CASTLE)n="O-O";else if(t.flags&fe.QSIDE_CASTLE)n="O-O-O";else{if(t.piece!==Et){const i=Jm(t,r);n+=t.piece.toUpperCase()+i}t.flags&(fe.CAPTURE|fe.EP_CAPTURE)&&(t.piece===Et&&(n+=ir(t.from)[0]),n+="x"),n+=ir(t.to),t.promotion&&(n+="="+t.promotion.toUpperCase())}return this._makeMove(t),this.isCheck()&&(this.isCheckmate()?n+="#":n+="+"),this._undoMove(),n}_moveFromSan(t,r=!1){const n=hu(t);let i=Nd(n),a=this._moves({legal:!0,piece:i});for(let d=0,v=a.length;d0?n+=this.perft(t-1):n++),this._undoMove();return n}_makePretty(t){const{color:r,piece:n,from:i,to:a,flags:s,captured:o,promotion:l}=t;let u="";for(const v in fe)fe[v]&s&&(u+=jm[v]);const c=ir(i),f=ir(a),d={color:r,piece:n,from:c,to:f,san:this._moveToSan(t,this._moves({legal:!0})),flags:u,lan:c+f,before:this.fen(),after:""};return this._makeMove(t),d.after=this.fen(),this._undoMove(),o&&(d.captured=o),l&&(d.promotion=l,d.lan+=l),d}turn(){return this._turn}board(){const t=[];let r=[];for(let n=ve.a8;n<=ve.h1;n++)this._board[n]==null?r.push(null):r.push({square:ir(n),type:this._board[n].type,color:this._board[n].color}),n+1&136&&(t.push(r),r=[],n+=8);return t}squareColor(t){if(t in ve){const r=ve[t];return(pi(r)+Cs(r))%2===0?"light":"dark"}return null}history({verbose:t=!1}={}){const r=[],n=[];for(;this._history.length>0;)r.push(this._undoMove());for(;;){const i=r.pop();if(!i)break;t?n.push(this._makePretty(i)):n.push(this._moveToSan(i,this._moves())),this._makeMove(i)}return n}_pruneComments(){const t=[],r={},n=i=>{i in this._comments&&(r[i]=this._comments[i])};for(;this._history.length>0;)t.push(this._undoMove());for(n(this.fen());;){const i=t.pop();if(!i)break;this._makeMove(i),n(this.fen())}this._comments=r}getComment(){return this._comments[this.fen()]}setComment(t){this._comments[this.fen()]=t.replace("{","[").replace("}","]")}deleteComment(){const t=this._comments[this.fen()];return delete this._comments[this.fen()],t}getComments(){return this._pruneComments(),Object.keys(this._comments).map(t=>({fen:t,comment:this._comments[t]}))}deleteComments(){return this._pruneComments(),Object.keys(this._comments).map(t=>{const r=this._comments[t];return delete this._comments[t],{fen:t,comment:r}})}setCastlingRights(t,r){for(const i of[St,kn])r[i]!==void 0&&(r[i]?this._castling[t]|=Ys[i]:this._castling[t]&=~Ys[i]);this._updateCastlingRights();const n=this.getCastlingRights(t);return(r[St]===void 0||r[St]===n[St])&&(r[kn]===void 0||r[kn]===n[kn])}getCastlingRights(t){return{[St]:(this._castling[t]&Ys[St])!==0,[kn]:(this._castling[t]&Ys[kn])!==0}}moveNumber(){return this._moveNumber}}const Qg=q.createContext({dragDropManager:void 0});function vr(e){return"Minified Redux error #"+e+"; visit https://redux.js.org/Errors?code="+e+" for the full message or use the non-minified dev environment for full errors. "}var Dd=typeof Symbol=="function"&&Symbol.observable||"@@observable",pu=function(){return Math.random().toString(36).substring(7).split("").join(".")},Rd={INIT:"@@redux/INIT"+pu(),REPLACE:"@@redux/REPLACE"+pu(),PROBE_UNKNOWN_ACTION:function(){return"@@redux/PROBE_UNKNOWN_ACTION"+pu()}};function Wg(e,t,r){var n;if(typeof t=="function"&&typeof r=="function"||typeof r=="function"&&typeof arguments[3]=="function")throw new Error(vr(0));if(typeof t=="function"&&r===void 0&&(r=t,t=void 0),r!==void 0){if(typeof r!="function")throw new Error(vr(1));return r(Wg)(e,t)}if(typeof e!="function")throw new Error(vr(2));var i=e,a=t,s=[],o=s,l=!1;function u(){o===s&&(o=s.slice())}function c(){if(l)throw new Error(vr(3));return a}function f(v){if(typeof v!="function")throw new Error(vr(4));if(l)throw new Error(vr(5));var g=!0;return u(),o.push(v),function(){if(g){if(l)throw new Error(vr(6));g=!1,u();var C=o.indexOf(v);o.splice(C,1),s=null}}}function d(v){if(!function(I){if(typeof I!="object"||I===null)return!1;for(var y=I;Object.getPrototypeOf(y)!==null;)y=Object.getPrototypeOf(y);return Object.getPrototypeOf(I)===y}(v))throw new Error(vr(7));if(v.type===void 0)throw new Error(vr(8));if(l)throw new Error(vr(9));try{l=!0,a=i(a,v)}finally{l=!1}for(var g=s=o,C=0;C=0;C--)if(v.canDragSource(d[C])){g=d[C];break}return g}(t,s);if(l==null)return void e.dispatch(t2);let u=null;if(i){if(!a)throw new Error("getSourceClientOffset must be defined");(function(d){de(typeof d=="function","When clientOffset is provided, getSourceClientOffset must be a function.")})(a),u=a(l)}e.dispatch(Ld(i,u));const c=o.getSource(l).beginDrag(s,l);if(c==null)return;(function(d){de(Yg(d),"Item must be an object.")})(c),o.pinSource(l);const f=o.getSourceType(l);return{type:qf,payload:{itemType:f,item:c,sourceId:l,clientOffset:i||null,sourceClientOffset:u||null,isSourcePublic:!!n}}}}function n2(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i2(e){for(var t=1;t{const o=function(u,c,f,d){const v=f.getTarget(u);let g=v?v.drop(d,u):void 0;return function(C){de(C===void 0||Yg(C),"Drop result must either be an object or undefined.")}(g),g===void 0&&(g=c===0?{}:d.getDropResult()),g}(a,s,n,r),l={type:$f,payload:{dropResult:i2({},t,o)}};e.dispatch(l)})}}function s2(e){return function(){const t=e.getMonitor(),r=e.getRegistry();(function(i){de(i.isDragging(),"Cannot call endDrag while not dragging.")})(t);const n=t.getSourceId();return n!=null&&(r.getSource(n,!0).endDrag(t,n),r.unpinSource()),{type:Gf}}}function wc(e,t){return t===null?e===null:Array.isArray(e)?e.some(r=>r===t):e===t}function o2(e){return function(t,{clientOffset:r}={}){(function(s){de(Array.isArray(s),"Expected targetIds to be an array.")})(t);const n=t.slice(0),i=e.getMonitor(),a=e.getRegistry();return function(s,o,l){for(let u=s.length-1;u>=0;u--){const c=s[u];wc(o.getTargetType(c),l)||s.splice(u,1)}}(n,a,i.getItemType()),function(s,o,l){de(o.isDragging(),"Cannot call hover while not dragging."),de(!o.didDrop(),"Cannot call hover after drop.");for(let u=0;u{const s=n[a];var o;return i[a]=(o=s,(...l)=>{const u=o.apply(t,l);u!==void 0&&r(u)}),i},{})}dispatch(t){this.store.dispatch(t)}constructor(t,r){this.isSetUp=!1,this.handleRefCountChange=()=>{const n=this.store.getState().refCount>0;this.backend&&(n&&!this.isSetUp?(this.backend.setup(),this.isSetUp=!0):!n&&this.isSetUp&&(this.backend.teardown(),this.isSetUp=!1))},this.store=t,this.monitor=r,t.subscribe(this.handleRefCountChange)}}function Od(e,t){return{x:e.x-t.x,y:e.y-t.y}}const Xa=[],Qf=[];Xa.__IS_NONE__=!0,Qf.__IS_ALL__=!0;class c2{subscribeToStateChange(t,r={}){const{handlerIds:n}=r;de(typeof t=="function","listener must be a function."),de(n===void 0||Array.isArray(n),"handlerIds, when specified, must be an array of strings.");let i=this.store.getState().stateId;return this.store.subscribe(()=>{const a=this.store.getState(),s=a.stateId;try{s===i||s===i+1&&!function(l,u){return l!==Xa&&(l===Qf||u===void 0||(c=l,u.filter(f=>c.indexOf(f)>-1)).length>0);var c}(a.dirtyHandlerIds,n)||t()}finally{i=s}})}subscribeToOffsetChange(t){de(typeof t=="function","listener must be a function.");let r=this.store.getState().dragOffset;return this.store.subscribe(()=>{const n=this.store.getState().dragOffset;n!==r&&(r=n,t())})}canDragSource(t){if(!t)return!1;const r=this.registry.getSource(t);return de(r,`Expected to find a valid source. sourceId=${t}`),!this.isDragging()&&r.canDrag(this,t)}canDropOnTarget(t){if(!t)return!1;const r=this.registry.getTarget(t);return de(r,`Expected to find a valid target. targetId=${t}`),!this.isDragging()||this.didDrop()?!1:wc(this.registry.getTargetType(t),this.getItemType())&&r.canDrop(this,t)}isDragging(){return!!this.getItemType()}isDraggingSource(t){if(!t)return!1;const r=this.registry.getSource(t,!0);return de(r,`Expected to find a valid source. sourceId=${t}`),!this.isDragging()||!this.isSourcePublic()?!1:this.registry.getSourceType(t)===this.getItemType()&&r.isDragging(this,t)}isOverTarget(t,r={shallow:!1}){if(!t)return!1;const{shallow:n}=r;if(!this.isDragging())return!1;const i=this.registry.getTargetType(t),a=this.getItemType();if(a&&!wc(i,a))return!1;const s=this.getTargetIds();if(!s.length)return!1;const o=s.indexOf(t);return n?o===s.length-1:o>-1}getItemType(){return this.store.getState().dragOperation.itemType}getItem(){return this.store.getState().dragOperation.item}getSourceId(){return this.store.getState().dragOperation.sourceId}getTargetIds(){return this.store.getState().dragOperation.targetIds}getDropResult(){return this.store.getState().dragOperation.dropResult}didDrop(){return this.store.getState().dragOperation.didDrop}isSourcePublic(){return!!this.store.getState().dragOperation.isSourcePublic}getInitialClientOffset(){return this.store.getState().dragOffset.initialClientOffset}getInitialSourceClientOffset(){return this.store.getState().dragOffset.initialSourceClientOffset}getClientOffset(){return this.store.getState().dragOffset.clientOffset}getSourceClientOffset(){return function(t){const{clientOffset:r,initialClientOffset:n,initialSourceClientOffset:i}=t;return r&&n&&i?Od((s=i,{x:(a=r).x+s.x,y:a.y+s.y}),n):null;var a,s}(this.store.getState().dragOffset)}getDifferenceFromInitialOffset(){return function(t){const{clientOffset:r,initialClientOffset:n}=t;return r&&n?Od(r,n):null}(this.store.getState().dragOffset)}constructor(t,r){this.store=t,this.registry=r}}const Pd=typeof global<"u"?global:self,Ud=Pd.MutationObserver||Pd.WebKitMutationObserver;function Zg(e){return function(){const t=setTimeout(n,0),r=setInterval(n,50);function n(){clearTimeout(t),clearInterval(r),e()}}}const f2=typeof Ud=="function"?function(e){let t=1;const r=new Ud(e),n=document.createTextNode("");return r.observe(n,{characterData:!0}),function(){t=-t,n.data=t}}:Zg;let d2=class{call(){try{this.task&&this.task()}catch(t){this.onError(t)}finally{this.task=null,this.release(this)}}constructor(t,r){this.onError=t,this.release=r,this.task=null}};const Jg=new class{enqueueTask(e){const{queue:t,requestFlush:r}=this;t.length||(r(),this.flushing=!0),t[t.length]=e}constructor(){this.queue=[],this.pendingErrors=[],this.flushing=!1,this.index=0,this.capacity=1024,this.flush=()=>{const{queue:e}=this;for(;this.indexthis.capacity){for(let r=0,n=e.length-this.index;r{this.pendingErrors.push(e),this.requestErrorThrow()},this.requestFlush=f2(this.flush),this.requestErrorThrow=Zg(()=>{if(this.pendingErrors.length)throw this.pendingErrors.shift()})}},h2=new class{create(e){const t=this.freeTasks,r=t.length?t.pop():new d2(this.onError,n=>t[t.length]=n);return r.task=e,r}constructor(e){this.onError=e,this.freeTasks=[]}}(Jg.registerPendingError),Wf="dnd-core/ADD_SOURCE",Yf="dnd-core/ADD_TARGET",Xf="dnd-core/REMOVE_SOURCE",Sl="dnd-core/REMOVE_TARGET";function Ic(e,t){t&&Array.isArray(e)?e.forEach(r=>Ic(r,!1)):de(typeof e=="string"||typeof e=="symbol",t?"Type can only be a string, a symbol, or an array of either.":"Type can only be a string or a symbol.")}var Tr;(function(e){e.SOURCE="SOURCE",e.TARGET="TARGET"})(Tr||(Tr={}));let p2=0;function g2(e){const t=(p2++).toString();switch(e){case Tr.SOURCE:return`S${t}`;case Tr.TARGET:return`T${t}`;default:throw new Error(`Unknown Handler Role: ${e}`)}}function Md(e){switch(e[0]){case"S":return Tr.SOURCE;case"T":return Tr.TARGET;default:throw new Error(`Cannot parse handler ID: ${e}`)}}function Fd(e,t){const r=e.entries();let n=!1;do{const{done:i,value:[,a]}=r.next();if(a===t)return!0;n=!!i}while(!n);return!1}class v2{addSource(t,r){Ic(t),function(i){de(typeof i.canDrag=="function","Expected canDrag to be a function."),de(typeof i.beginDrag=="function","Expected beginDrag to be a function."),de(typeof i.endDrag=="function","Expected endDrag to be a function.")}(r);const n=this.addHandler(Tr.SOURCE,t,r);return this.store.dispatch(function(i){return{type:Wf,payload:{sourceId:i}}}(n)),n}addTarget(t,r){Ic(t,!0),function(i){de(typeof i.canDrop=="function","Expected canDrop to be a function."),de(typeof i.hover=="function","Expected hover to be a function."),de(typeof i.drop=="function","Expected beginDrag to be a function.")}(r);const n=this.addHandler(Tr.TARGET,t,r);return this.store.dispatch(function(i){return{type:Yf,payload:{targetId:i}}}(n)),n}containsHandler(t){return Fd(this.dragSources,t)||Fd(this.dropTargets,t)}getSource(t,r=!1){return de(this.isSourceId(t),"Expected a valid source ID."),r&&t===this.pinnedSourceId?this.pinnedSource:this.dragSources.get(t)}getTarget(t){return de(this.isTargetId(t),"Expected a valid target ID."),this.dropTargets.get(t)}getSourceType(t){return de(this.isSourceId(t),"Expected a valid source ID."),this.types.get(t)}getTargetType(t){return de(this.isTargetId(t),"Expected a valid target ID."),this.types.get(t)}isSourceId(t){return Md(t)===Tr.SOURCE}isTargetId(t){return Md(t)===Tr.TARGET}removeSource(t){var r;de(this.getSource(t),"Expected an existing source."),this.store.dispatch(function(n){return{type:Xf,payload:{sourceId:n}}}(t)),r=()=>{this.dragSources.delete(t),this.types.delete(t)},Jg.enqueueTask(h2.create(r))}removeTarget(t){de(this.getTarget(t),"Expected an existing target."),this.store.dispatch(function(r){return{type:Sl,payload:{targetId:r}}}(t)),this.dropTargets.delete(t),this.types.delete(t)}pinSource(t){const r=this.getSource(t);de(r,"Expected an existing source."),this.pinnedSourceId=t,this.pinnedSource=r}unpinSource(){de(this.pinnedSource,"No source is pinned at the time."),this.pinnedSourceId=null,this.pinnedSource=null}addHandler(t,r,n){const i=g2(t);return this.types.set(i,r),t===Tr.SOURCE?this.dragSources.set(i,n):t===Tr.TARGET&&this.dropTargets.set(i,n),i}constructor(t){this.types=new Map,this.dragSources=new Map,this.dropTargets=new Map,this.pinnedSourceId=null,this.pinnedSource=null,this.store=t}}const y2=(e,t)=>e===t;function m2(e=Xa,t){switch(t.type){case El:break;case Wf:case Yf:case Sl:case Xf:return Xa;default:return Qf}const{targetIds:r=[],prevTargetIds:n=[]}=t.payload,i=function(o,l){const u=new Map,c=d=>{u.set(d,u.has(d)?u.get(d)+1:1)};o.forEach(c),l.forEach(c);const f=[];return u.forEach((d,v)=>{d===1&&f.push(v)}),f}(r,n);if(!(i.length>0||!function(o,l,u=y2){if(o.length!==l.length)return!1;for(let c=0;ca!==i))});case $f:return bi({},e,{dropResult:r.dropResult,didDrop:!0,targetIds:[]});case Gf:return bi({},e,{itemType:null,item:null,sourceId:null,dropResult:null,didDrop:!1,isSourcePublic:null,targetIds:[]});default:return e}var n,i}function w2(e=0,t){switch(t.type){case Wf:case Yf:return e+1;case Xf:case Sl:return e-1;default:return e}}function I2(e=0){return e+1}function _2(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function A2(e){for(var t=1;ta&&a[s]?a[s]:i||null,r))})}),dragOffset:E2(e.dragOffset,t),refCount:w2(e.refCount,t),dragOperation:T2(e.dragOperation,t),stateId:I2(e.stateId)};var r,n,i}function k2(e,t=void 0,r={},n=!1){const i=function(l){const u=typeof window<"u"&&window.__REDUX_DEVTOOLS_EXTENSION__;return Wg(B2,l&&u&&u({name:"dnd-core",instanceId:"dnd-core"}))}(n),a=new c2(i,new v2(i)),s=new u2(i,a),o=e(s,t,r);return s.receiveBackend(o),s}function b2(e,t){if(e==null)return{};var r,n,i=function(s,o){if(s==null)return{};var l,u,c={},f=Object.keys(s);for(u=0;u=0||(c[l]=s[l]);return c}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}let jd=0;const Xs=Symbol.for("__REACT_DND_CONTEXT_INSTANCE__");var N2=q.memo(function(e){var{children:t}=e,r=b2(e,["children"]);const[n,i]=function(a){if("manager"in a)return[{dragDropManager:a.manager},!1];const s=function(l,u=Kd(),c,f){const d=u;return d[Xs]||(d[Xs]={dragDropManager:k2(l,u,c,f)}),d[Xs]}(a.backend,a.context,a.options,a.debugMode),o=!a.context;return[s,o]}(r);return q.useEffect(()=>{if(i){const a=Kd();return++jd,()=>{--jd==0&&(a[Xs]=null)}}},[]),D.jsx(Qg.Provider,{value:n,children:t})});function Kd(){return typeof global<"u"?global:window}var D2=function e(t,r){if(t===r)return!0;if(t&&r&&typeof t=="object"&&typeof r=="object"){if(t.constructor!==r.constructor)return!1;var n,i,a;if(Array.isArray(t)){if((n=t.length)!=r.length)return!1;for(i=n;i--!=0;)if(!e(t[i],r[i]))return!1;return!0}if(t.constructor===RegExp)return t.source===r.source&&t.flags===r.flags;if(t.valueOf!==Object.prototype.valueOf)return t.valueOf()===r.valueOf();if(t.toString!==Object.prototype.toString)return t.toString()===r.toString();if((n=(a=Object.keys(t)).length)!==Object.keys(r).length)return!1;for(i=n;i--!=0;)if(!Object.prototype.hasOwnProperty.call(r,a[i]))return!1;for(i=n;i--!=0;){var s=a[i];if(!e(t[s],r[s]))return!1}return!0}return t!=t&&r!=r};const Si=typeof window<"u"?q.useLayoutEffect:q.useEffect;function e1(e,t,r){const[n,i]=q.useState(()=>t(e)),a=q.useCallback(()=>{const s=t(e);D2(n,s)||(i(s),r&&r())},[n,e,r]);return Si(a),[n,a]}function t1(e,t,r){return function(n,i,a){const[s,o]=e1(n,i,a);return Si(function(){const l=n.getHandlerId();if(l!=null)return n.subscribeToStateChange(o,{handlerIds:[l]})},[n,o]),s}(t,e||(()=>({})),()=>r.reconnect())}function r1(e,t){const r=[...t||[]];return t==null&&typeof e!="function"&&r.push(e),q.useMemo(()=>typeof e=="function"?e():e,r)}function R2(e){return q.useMemo(()=>e.hooks.dragSource(),[e])}function L2(e){return q.useMemo(()=>e.hooks.dragPreview(),[e])}let gu=!1,vu=!1;class O2{receiveHandlerId(t){this.sourceId=t}getHandlerId(){return this.sourceId}canDrag(){de(!gu,"You may not call monitor.canDrag() inside your canDrag() implementation. Read more: http://react-dnd.github.io/react-dnd/docs/api/drag-source-monitor");try{return gu=!0,this.internalMonitor.canDragSource(this.sourceId)}finally{gu=!1}}isDragging(){if(!this.sourceId)return!1;de(!vu,"You may not call monitor.isDragging() inside your isDragging() implementation. Read more: http://react-dnd.github.io/react-dnd/docs/api/drag-source-monitor");try{return vu=!0,this.internalMonitor.isDraggingSource(this.sourceId)}finally{vu=!1}}subscribeToStateChange(t,r){return this.internalMonitor.subscribeToStateChange(t,r)}isDraggingSource(t){return this.internalMonitor.isDraggingSource(t)}isOverTarget(t,r){return this.internalMonitor.isOverTarget(t,r)}getTargetIds(){return this.internalMonitor.getTargetIds()}isSourcePublic(){return this.internalMonitor.isSourcePublic()}getSourceId(){return this.internalMonitor.getSourceId()}subscribeToOffsetChange(t){return this.internalMonitor.subscribeToOffsetChange(t)}canDragSource(t){return this.internalMonitor.canDragSource(t)}canDropOnTarget(t){return this.internalMonitor.canDropOnTarget(t)}getItemType(){return this.internalMonitor.getItemType()}getItem(){return this.internalMonitor.getItem()}getDropResult(){return this.internalMonitor.getDropResult()}didDrop(){return this.internalMonitor.didDrop()}getInitialClientOffset(){return this.internalMonitor.getInitialClientOffset()}getInitialSourceClientOffset(){return this.internalMonitor.getInitialSourceClientOffset()}getSourceClientOffset(){return this.internalMonitor.getSourceClientOffset()}getClientOffset(){return this.internalMonitor.getClientOffset()}getDifferenceFromInitialOffset(){return this.internalMonitor.getDifferenceFromInitialOffset()}constructor(t){this.sourceId=null,this.internalMonitor=t.getMonitor()}}let yu=!1;class P2{receiveHandlerId(t){this.targetId=t}getHandlerId(){return this.targetId}subscribeToStateChange(t,r){return this.internalMonitor.subscribeToStateChange(t,r)}canDrop(){if(!this.targetId)return!1;de(!yu,"You may not call monitor.canDrop() inside your canDrop() implementation. Read more: http://react-dnd.github.io/react-dnd/docs/api/drop-target-monitor");try{return yu=!0,this.internalMonitor.canDropOnTarget(this.targetId)}finally{yu=!1}}isOver(t){return!!this.targetId&&this.internalMonitor.isOverTarget(this.targetId,t)}getItemType(){return this.internalMonitor.getItemType()}getItem(){return this.internalMonitor.getItem()}getDropResult(){return this.internalMonitor.getDropResult()}didDrop(){return this.internalMonitor.didDrop()}getInitialClientOffset(){return this.internalMonitor.getInitialClientOffset()}getInitialSourceClientOffset(){return this.internalMonitor.getInitialSourceClientOffset()}getSourceClientOffset(){return this.internalMonitor.getSourceClientOffset()}getClientOffset(){return this.internalMonitor.getClientOffset()}getDifferenceFromInitialOffset(){return this.internalMonitor.getDifferenceFromInitialOffset()}constructor(t){this.targetId=null,this.internalMonitor=t.getMonitor()}}function _c(e,t,r,n){let i=r?r.call(n,e,t):void 0;if(i!==void 0)return!!i;if(e===t)return!0;if(typeof e!="object"||!e||typeof t!="object"||!t)return!1;const a=Object.keys(e),s=Object.keys(t);if(a.length!==s.length)return!1;const o=Object.prototype.hasOwnProperty.bind(t);for(let l=0;l{if(!q.isValidElement(t)){const i=t;return e(i,r),i}const n=t;return function(i){if(typeof i.type=="string")return;const a=i.type.displayName||i.type.name||"the component";throw new Error(`Only native element nodes can now be passed to React DnD connectors.You can either wrap ${a} into a
, or turn it into a drag source or a drop target itself.`)}(n),function(i,a){const s=i.ref;return de(typeof s!="string","Cannot connect React DnD to an element with an existing string ref. Please convert it to use a callback ref instead, or wrap it into a or
. Read more: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs"),q.cloneElement(i,s?{ref:o=>{zd(s,o),zd(a,o)}}:{ref:a})}(n,r?i=>e(i,r):e)}}function n1(e){const t={};return Object.keys(e).forEach(r=>{const n=e[r];if(r.endsWith("Ref"))t[r]=e[r];else{const i=U2(n);t[r]=()=>i}}),t}function zd(e,t){typeof e=="function"?e(t):e.current=t}class M2{receiveHandlerId(t){this.handlerId!==t&&(this.handlerId=t,this.reconnect())}get connectTarget(){return this.dragSource}get dragSourceOptions(){return this.dragSourceOptionsInternal}set dragSourceOptions(t){this.dragSourceOptionsInternal=t}get dragPreviewOptions(){return this.dragPreviewOptionsInternal}set dragPreviewOptions(t){this.dragPreviewOptionsInternal=t}reconnect(){const t=this.reconnectDragSource();this.reconnectDragPreview(t)}reconnectDragSource(){const t=this.dragSource,r=this.didHandlerIdChange()||this.didConnectedDragSourceChange()||this.didDragSourceOptionsChange();return r&&this.disconnectDragSource(),this.handlerId?t?(r&&(this.lastConnectedHandlerId=this.handlerId,this.lastConnectedDragSource=t,this.lastConnectedDragSourceOptions=this.dragSourceOptions,this.dragSourceUnsubscribe=this.backend.connectDragSource(this.handlerId,t,this.dragSourceOptions)),r):(this.lastConnectedDragSource=t,r):r}reconnectDragPreview(t=!1){const r=this.dragPreview,n=t||this.didHandlerIdChange()||this.didConnectedDragPreviewChange()||this.didDragPreviewOptionsChange();n&&this.disconnectDragPreview(),this.handlerId&&(r?n&&(this.lastConnectedHandlerId=this.handlerId,this.lastConnectedDragPreview=r,this.lastConnectedDragPreviewOptions=this.dragPreviewOptions,this.dragPreviewUnsubscribe=this.backend.connectDragPreview(this.handlerId,r,this.dragPreviewOptions)):this.lastConnectedDragPreview=r)}didHandlerIdChange(){return this.lastConnectedHandlerId!==this.handlerId}didConnectedDragSourceChange(){return this.lastConnectedDragSource!==this.dragSource}didConnectedDragPreviewChange(){return this.lastConnectedDragPreview!==this.dragPreview}didDragSourceOptionsChange(){return!_c(this.lastConnectedDragSourceOptions,this.dragSourceOptions)}didDragPreviewOptionsChange(){return!_c(this.lastConnectedDragPreviewOptions,this.dragPreviewOptions)}disconnectDragSource(){this.dragSourceUnsubscribe&&(this.dragSourceUnsubscribe(),this.dragSourceUnsubscribe=void 0)}disconnectDragPreview(){this.dragPreviewUnsubscribe&&(this.dragPreviewUnsubscribe(),this.dragPreviewUnsubscribe=void 0,this.dragPreviewNode=null,this.dragPreviewRef=null)}get dragSource(){return this.dragSourceNode||this.dragSourceRef&&this.dragSourceRef.current}get dragPreview(){return this.dragPreviewNode||this.dragPreviewRef&&this.dragPreviewRef.current}clearDragSource(){this.dragSourceNode=null,this.dragSourceRef=null}clearDragPreview(){this.dragPreviewNode=null,this.dragPreviewRef=null}constructor(t){this.hooks=n1({dragSource:(r,n)=>{this.clearDragSource(),this.dragSourceOptions=n||null,Ac(r)?this.dragSourceRef=r:this.dragSourceNode=r,this.reconnectDragSource()},dragPreview:(r,n)=>{this.clearDragPreview(),this.dragPreviewOptions=n||null,Ac(r)?this.dragPreviewRef=r:this.dragPreviewNode=r,this.reconnectDragPreview()}}),this.handlerId=null,this.dragSourceRef=null,this.dragSourceOptionsInternal=null,this.dragPreviewRef=null,this.dragPreviewOptionsInternal=null,this.lastConnectedHandlerId=null,this.lastConnectedDragSource=null,this.lastConnectedDragSourceOptions=null,this.lastConnectedDragPreview=null,this.lastConnectedDragPreviewOptions=null,this.backend=t}}class F2{get connectTarget(){return this.dropTarget}reconnect(){const t=this.didHandlerIdChange()||this.didDropTargetChange()||this.didOptionsChange();t&&this.disconnectDropTarget();const r=this.dropTarget;this.handlerId&&(r?t&&(this.lastConnectedHandlerId=this.handlerId,this.lastConnectedDropTarget=r,this.lastConnectedDropTargetOptions=this.dropTargetOptions,this.unsubscribeDropTarget=this.backend.connectDropTarget(this.handlerId,r,this.dropTargetOptions)):this.lastConnectedDropTarget=r)}receiveHandlerId(t){t!==this.handlerId&&(this.handlerId=t,this.reconnect())}get dropTargetOptions(){return this.dropTargetOptionsInternal}set dropTargetOptions(t){this.dropTargetOptionsInternal=t}didHandlerIdChange(){return this.lastConnectedHandlerId!==this.handlerId}didDropTargetChange(){return this.lastConnectedDropTarget!==this.dropTarget}didOptionsChange(){return!_c(this.lastConnectedDropTargetOptions,this.dropTargetOptions)}disconnectDropTarget(){this.unsubscribeDropTarget&&(this.unsubscribeDropTarget(),this.unsubscribeDropTarget=void 0)}get dropTarget(){return this.dropTargetNode||this.dropTargetRef&&this.dropTargetRef.current}clearDropTarget(){this.dropTargetRef=null,this.dropTargetNode=null}constructor(t){this.hooks=n1({dropTarget:(r,n)=>{this.clearDropTarget(),this.dropTargetOptions=n,Ac(r)?this.dropTargetRef=r:this.dropTargetNode=r,this.reconnect()}}),this.handlerId=null,this.dropTargetRef=null,this.dropTargetOptionsInternal=null,this.lastConnectedHandlerId=null,this.lastConnectedDropTarget=null,this.lastConnectedDropTargetOptions=null,this.backend=t}}function xi(){const{dragDropManager:e}=q.useContext(Qg);return de(e!=null,"Expected drag drop context"),e}class V2{beginDrag(){const t=this.spec,r=this.monitor;let n=null;return n=typeof t.item=="object"?t.item:typeof t.item=="function"?t.item(r):{},n??null}canDrag(){const t=this.spec,r=this.monitor;return typeof t.canDrag=="boolean"?t.canDrag:typeof t.canDrag!="function"||t.canDrag(r)}isDragging(t,r){const n=this.spec,i=this.monitor,{isDragging:a}=n;return a?a(i):r===t.getSourceId()}endDrag(){const t=this.spec,r=this.monitor,n=this.connector,{end:i}=t;i&&i(r.getItem(),r),n.reconnect()}constructor(t,r,n){this.spec=t,this.monitor=r,this.connector=n}}function j2(e,t,r){const n=xi(),i=function(s,o,l){const u=q.useMemo(()=>new V2(s,o,l),[o,l]);return q.useEffect(()=>{u.spec=s},[s]),u}(e,t,r),a=function(s){return q.useMemo(()=>{const o=s.type;return de(o!=null,"spec.type must be defined"),o},[s])}(e);Si(function(){if(a!=null){const[s,o]=function(l,u,c){const f=c.getRegistry(),d=f.addSource(l,u);return[d,()=>f.removeSource(d)]}(a,i,n);return t.receiveHandlerId(s),r.receiveHandlerId(s),o}},[n,t,r,i,a])}function K2(e,t){const r=r1(e,t);de(!r.begin,"useDrag::spec.begin was deprecated in v14. Replace spec.begin() with spec.item(). (see more here - https://react-dnd.github.io/react-dnd/docs/api/use-drag)");const n=function(){const a=xi();return q.useMemo(()=>new O2(a),[a])}(),i=function(a,s){const o=xi(),l=q.useMemo(()=>new M2(o.getBackend()),[o]);return Si(()=>(l.dragSourceOptions=a||null,l.reconnect(),()=>l.disconnectDragSource()),[l,a]),Si(()=>(l.dragPreviewOptions=s||null,l.reconnect(),()=>l.disconnectDragPreview()),[l,s]),l}(r.options,r.previewOptions);return j2(r,n,i),[t1(r.collect,n,i),R2(i),L2(i)]}function z2(e){return q.useMemo(()=>e.hooks.dropTarget(),[e])}class H2{canDrop(){const t=this.spec,r=this.monitor;return!t.canDrop||t.canDrop(r.getItem(),r)}hover(){const t=this.spec,r=this.monitor;t.hover&&t.hover(r.getItem(),r)}drop(){const t=this.spec,r=this.monitor;if(t.drop)return t.drop(r.getItem(),r)}constructor(t,r){this.spec=t,this.monitor=r}}function q2(e,t,r){const n=xi(),i=function(s,o){const l=q.useMemo(()=>new H2(s,o),[o]);return q.useEffect(()=>{l.spec=s},[s]),l}(e,t),a=function(s){const{accept:o}=s;return q.useMemo(()=>(de(s.accept!=null,"accept must be defined"),Array.isArray(o)?o:[o]),[o])}(e);Si(function(){const[s,o]=function(l,u,c){const f=c.getRegistry(),d=f.addTarget(l,u);return[d,()=>f.removeTarget(d)]}(a,i,n);return t.receiveHandlerId(s),r.receiveHandlerId(s),o},[n,t,i,r,a.map(s=>s.toString()).join("|")])}function $2(e,t){const r=r1(e,t),n=function(){const a=xi();return q.useMemo(()=>new P2(a),[a])}(),i=function(a){const s=xi(),o=q.useMemo(()=>new F2(s.getBackend()),[s]);return Si(()=>(o.dropTargetOptions=a||null,o.reconnect(),()=>o.disconnectDropTarget()),[a]),o}(r.options);return q2(r,n,i),[t1(r.collect,n,i),z2(i)]}function i1(e){let t=null;return()=>(t==null&&(t=e()),t)}class G2{enter(t){const r=this.entered.length;return this.entered=function(n,i){const a=new Set,s=l=>a.add(l);n.forEach(s),i.forEach(s);const o=[];return a.forEach(l=>o.push(l)),o}(this.entered.filter(n=>this.isNodeInDocument(n)&&(!n.contains||n.contains(t))),[t]),r===0&&this.entered.length>0}leave(t){const r=this.entered.length;var n,i;return this.entered=(n=this.entered.filter(this.isNodeInDocument),i=t,n.filter(a=>a!==i)),r>0&&this.entered.length===0}reset(){this.entered=[]}constructor(t){this.entered=[],this.isNodeInDocument=t}}class Q2{initializeExposedProperties(){Object.keys(this.config.exposeProperties).forEach(t=>{Object.defineProperty(this.item,t,{configurable:!0,enumerable:!0,get:()=>(console.warn(`Browser doesn't allow reading "${t}" until the drop event.`),null)})})}loadDataTransfer(t){if(t){const r={};Object.keys(this.config.exposeProperties).forEach(n=>{const i=this.config.exposeProperties[n];i!=null&&(r[n]={value:i(t,this.config.matchesTypes),configurable:!0,enumerable:!0})}),Object.defineProperties(this.item,r)}}canDrag(){return!0}beginDrag(){return this.item}isDragging(t,r){return r===t.getSourceId()}endDrag(){}constructor(t){this.config=t,this.item={},this.initializeExposedProperties()}}const a1="__NATIVE_FILE__",s1="__NATIVE_URL__",o1="__NATIVE_TEXT__",l1="__NATIVE_HTML__";var Hd=Object.freeze({__proto__:null,FILE:a1,HTML:l1,TEXT:o1,URL:s1});function mu(e,t,r){const n=t.reduce((i,a)=>i||e.getData(a),"");return n??r}const Bc={[a1]:{exposeProperties:{files:e=>Array.prototype.slice.call(e.files),items:e=>e.items,dataTransfer:e=>e},matchesTypes:["Files"]},[l1]:{exposeProperties:{html:(e,t)=>mu(e,t,""),dataTransfer:e=>e},matchesTypes:["Html","text/html"]},[s1]:{exposeProperties:{urls:(e,t)=>mu(e,t,"").split(` -`),dataTransfer:e=>e},matchesTypes:["Url","text/uri-list"]},[o1]:{exposeProperties:{text:(e,t)=>mu(e,t,""),dataTransfer:e=>e},matchesTypes:["Text","text/plain"]}};function Cu(e){if(!e)return null;const t=Array.prototype.slice.call(e.types||[]);return Object.keys(Bc).filter(r=>{const n=Bc[r];return!!(n!=null&&n.matchesTypes)&&n.matchesTypes.some(i=>t.indexOf(i)>-1)})[0]||null}const W2=i1(()=>/firefox/i.test(navigator.userAgent)),qd=i1(()=>!!window.safari);class $d{interpolate(t){const{xs:r,ys:n,c1s:i,c2s:a,c3s:s}=this;let o=r.length-1;if(t===r[o])return n[o];let l,u=0,c=s.length-1;for(;u<=c;){l=Math.floor(.5*(u+c));const v=r[l];if(vt))return n[l];c=l-1}}o=Math.max(0,c);const f=t-r[o],d=f*f;return n[o]+i[o]*f+a[o]*d+s[o]*f*d}constructor(t,r){const{length:n}=t,i=[];for(let v=0;vt[v]{let S=new $d([0,.5,1],[u.y,u.y/f*C,u.y+C-f]).interpolate(v);return qd()&&a&&(S+=(window.devicePixelRatio-1)*C),S})()}}let Js;function Z2(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function Gd(e){for(var t=1;t{this.sourcePreviewNodes.delete(t),this.sourcePreviewNodeOptions.delete(t)}}connectDragSource(t,r,n){this.sourceNodes.set(t,r),this.sourceNodeOptions.set(t,n);const i=s=>this.handleDragStart(s,t),a=s=>this.handleSelectStart(s);return r.setAttribute("draggable","true"),r.addEventListener("dragstart",i),r.addEventListener("selectstart",a),()=>{this.sourceNodes.delete(t),this.sourceNodeOptions.delete(t),r.removeEventListener("dragstart",i),r.removeEventListener("selectstart",a),r.setAttribute("draggable","false")}}connectDropTarget(t,r){const n=s=>this.handleDragEnter(s,t),i=s=>this.handleDragOver(s,t),a=s=>this.handleDrop(s,t);return r.addEventListener("dragenter",n),r.addEventListener("dragover",i),r.addEventListener("drop",a),()=>{r.removeEventListener("dragenter",n),r.removeEventListener("dragover",i),r.removeEventListener("drop",a)}}addEventListeners(t){t.addEventListener&&(t.addEventListener("dragstart",this.handleTopDragStart),t.addEventListener("dragstart",this.handleTopDragStartCapture,!0),t.addEventListener("dragend",this.handleTopDragEndCapture,!0),t.addEventListener("dragenter",this.handleTopDragEnter),t.addEventListener("dragenter",this.handleTopDragEnterCapture,!0),t.addEventListener("dragleave",this.handleTopDragLeaveCapture,!0),t.addEventListener("dragover",this.handleTopDragOver),t.addEventListener("dragover",this.handleTopDragOverCapture,!0),t.addEventListener("drop",this.handleTopDrop),t.addEventListener("drop",this.handleTopDropCapture,!0))}removeEventListeners(t){t.removeEventListener&&(t.removeEventListener("dragstart",this.handleTopDragStart),t.removeEventListener("dragstart",this.handleTopDragStartCapture,!0),t.removeEventListener("dragend",this.handleTopDragEndCapture,!0),t.removeEventListener("dragenter",this.handleTopDragEnter),t.removeEventListener("dragenter",this.handleTopDragEnterCapture,!0),t.removeEventListener("dragleave",this.handleTopDragLeaveCapture,!0),t.removeEventListener("dragover",this.handleTopDragOver),t.removeEventListener("dragover",this.handleTopDragOverCapture,!0),t.removeEventListener("drop",this.handleTopDrop),t.removeEventListener("drop",this.handleTopDropCapture,!0))}getCurrentSourceNodeOptions(){const t=this.monitor.getSourceId(),r=this.sourceNodeOptions.get(t);return Gd({dropEffect:this.altKeyPressed?"copy":"move"},r||{})}getCurrentDropEffect(){return this.isDraggingNativeItem()?"copy":this.getCurrentSourceNodeOptions().dropEffect}getCurrentSourcePreviewNodeOptions(){const t=this.monitor.getSourceId();return Gd({anchorX:.5,anchorY:.5,captureDraggingState:!1},this.sourcePreviewNodeOptions.get(t)||{})}isDraggingNativeItem(){const t=this.monitor.getItemType();return Object.keys(Hd).some(r=>Hd[r]===t)}beginDragNativeItem(t,r){this.clearCurrentDragSourceNode(),this.currentNativeSource=function(n,i){const a=Bc[n];if(!a)throw new Error(`native type ${n} has no configuration`);const s=new Q2(a);return s.loadDataTransfer(i),s}(t,r),this.currentNativeHandle=this.registry.addSource(t,this.currentNativeSource),this.actions.beginDrag([this.currentNativeHandle])}setCurrentDragSourceNode(t){this.clearCurrentDragSourceNode(),this.currentDragSourceNode=t,this.mouseMoveTimeoutTimer=setTimeout(()=>{var r;return(r=this.rootElement)===null||r===void 0?void 0:r.addEventListener("mousemove",this.endDragIfSourceWasRemovedFromDOM,!0)},1e3)}clearCurrentDragSourceNode(){if(this.currentDragSourceNode){var t;return this.currentDragSourceNode=null,this.rootElement&&((t=this.window)===null||t===void 0||t.clearTimeout(this.mouseMoveTimeoutTimer||void 0),this.rootElement.removeEventListener("mousemove",this.endDragIfSourceWasRemovedFromDOM,!0)),this.mouseMoveTimeoutTimer=null,!0}return!1}handleDragStart(t,r){t.defaultPrevented||(this.dragStartSourceIds||(this.dragStartSourceIds=[]),this.dragStartSourceIds.unshift(r))}handleDragEnter(t,r){this.dragEnterTargetIds.unshift(r)}handleDragOver(t,r){this.dragOverTargetIds===null&&(this.dragOverTargetIds=[]),this.dragOverTargetIds.unshift(r)}handleDrop(t,r){this.dropTargetIds.unshift(r)}constructor(t,r,n){this.sourcePreviewNodes=new Map,this.sourcePreviewNodeOptions=new Map,this.sourceNodes=new Map,this.sourceNodeOptions=new Map,this.dragStartSourceIds=null,this.dropTargetIds=[],this.dragEnterTargetIds=[],this.currentNativeSource=null,this.currentNativeHandle=null,this.currentDragSourceNode=null,this.altKeyPressed=!1,this.mouseMoveTimeoutTimer=null,this.asyncEndDragFrameId=null,this.dragOverTargetIds=null,this.lastClientOffset=null,this.hoverRafId=null,this.getSourceClientOffset=i=>{const a=this.sourceNodes.get(i);return a&&u1(a)||null},this.endDragNativeItem=()=>{this.isDraggingNativeItem()&&(this.actions.endDrag(),this.currentNativeHandle&&this.registry.removeSource(this.currentNativeHandle),this.currentNativeHandle=null,this.currentNativeSource=null)},this.isNodeInDocument=i=>!!(i&&this.document&&this.document.body&&this.document.body.contains(i)),this.endDragIfSourceWasRemovedFromDOM=()=>{const i=this.currentDragSourceNode;i==null||this.isNodeInDocument(i)||(this.clearCurrentDragSourceNode()&&this.monitor.isDragging()&&this.actions.endDrag(),this.cancelHover())},this.scheduleHover=i=>{this.hoverRafId===null&&typeof requestAnimationFrame<"u"&&(this.hoverRafId=requestAnimationFrame(()=>{this.monitor.isDragging()&&this.actions.hover(i||[],{clientOffset:this.lastClientOffset}),this.hoverRafId=null}))},this.cancelHover=()=>{this.hoverRafId!==null&&typeof cancelAnimationFrame<"u"&&(cancelAnimationFrame(this.hoverRafId),this.hoverRafId=null)},this.handleTopDragStartCapture=()=>{this.clearCurrentDragSourceNode(),this.dragStartSourceIds=[]},this.handleTopDragStart=i=>{if(i.defaultPrevented)return;const{dragStartSourceIds:a}=this;this.dragStartSourceIds=null;const s=Zs(i);this.monitor.isDragging()&&(this.actions.endDrag(),this.cancelHover()),this.actions.beginDrag(a||[],{publishSource:!1,getSourceClientOffset:this.getSourceClientOffset,clientOffset:s});const{dataTransfer:o}=i,l=Cu(o);if(this.monitor.isDragging()){if(o&&typeof o.setDragImage=="function"){const c=this.monitor.getSourceId(),f=this.sourceNodes.get(c),d=this.sourcePreviewNodes.get(c)||f;if(d){const{anchorX:v,anchorY:g,offsetX:C,offsetY:I}=this.getCurrentSourcePreviewNodeOptions(),y=X2(f,d,s,{anchorX:v,anchorY:g},{offsetX:C,offsetY:I});o.setDragImage(d,y.x,y.y)}}try{o==null||o.setData("application/json",{})}catch{}this.setCurrentDragSourceNode(i.target);const{captureDraggingState:u}=this.getCurrentSourcePreviewNodeOptions();u?this.actions.publishDragSource():setTimeout(()=>this.actions.publishDragSource(),0)}else if(l)this.beginDragNativeItem(l);else{if(o&&!o.types&&(i.target&&!i.target.hasAttribute||!i.target.hasAttribute("draggable")))return;i.preventDefault()}},this.handleTopDragEndCapture=()=>{this.clearCurrentDragSourceNode()&&this.monitor.isDragging()&&this.actions.endDrag(),this.cancelHover()},this.handleTopDragEnterCapture=i=>{var a;if(this.dragEnterTargetIds=[],this.isDraggingNativeItem()&&((a=this.currentNativeSource)===null||a===void 0||a.loadDataTransfer(i.dataTransfer)),!this.enterLeaveCounter.enter(i.target)||this.monitor.isDragging())return;const{dataTransfer:s}=i,o=Cu(s);o&&this.beginDragNativeItem(o,s)},this.handleTopDragEnter=i=>{const{dragEnterTargetIds:a}=this;this.dragEnterTargetIds=[],this.monitor.isDragging()&&(this.altKeyPressed=i.altKey,a.length>0&&this.actions.hover(a,{clientOffset:Zs(i)}),a.some(s=>this.monitor.canDropOnTarget(s))&&(i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect=this.getCurrentDropEffect())))},this.handleTopDragOverCapture=i=>{var a;this.dragOverTargetIds=[],this.isDraggingNativeItem()&&((a=this.currentNativeSource)===null||a===void 0||a.loadDataTransfer(i.dataTransfer))},this.handleTopDragOver=i=>{const{dragOverTargetIds:a}=this;if(this.dragOverTargetIds=[],!this.monitor.isDragging())return i.preventDefault(),void(i.dataTransfer&&(i.dataTransfer.dropEffect="none"));this.altKeyPressed=i.altKey,this.lastClientOffset=Zs(i),this.scheduleHover(a),(a||[]).some(s=>this.monitor.canDropOnTarget(s))?(i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect=this.getCurrentDropEffect())):this.isDraggingNativeItem()?i.preventDefault():(i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="none"))},this.handleTopDragLeaveCapture=i=>{this.isDraggingNativeItem()&&i.preventDefault(),this.enterLeaveCounter.leave(i.target)&&(this.isDraggingNativeItem()&&setTimeout(()=>this.endDragNativeItem(),0),this.cancelHover())},this.handleTopDropCapture=i=>{var a;this.dropTargetIds=[],this.isDraggingNativeItem()?(i.preventDefault(),(a=this.currentNativeSource)===null||a===void 0||a.loadDataTransfer(i.dataTransfer)):Cu(i.dataTransfer)&&i.preventDefault(),this.enterLeaveCounter.reset()},this.handleTopDrop=i=>{const{dropTargetIds:a}=this;this.dropTargetIds=[],this.actions.hover(a,{clientOffset:Zs(i)}),this.actions.drop({dropEffect:this.getCurrentDropEffect()}),this.isDraggingNativeItem()?this.endDragNativeItem():this.monitor.isDragging()&&this.actions.endDrag(),this.cancelHover()},this.handleSelectStart=i=>{const a=i.target;typeof a.dragDrop=="function"&&(a.tagName==="INPUT"||a.tagName==="SELECT"||a.tagName==="TEXTAREA"||a.isContentEditable||(i.preventDefault(),a.dragDrop()))},this.options=new class{get window(){return this.globalContext?this.globalContext:typeof window<"u"?window:void 0}get document(){var i;return!((i=this.globalContext)===null||i===void 0)&&i.document?this.globalContext.document:this.window?this.window.document:void 0}get rootElement(){var i;return((i=this.optionsArgs)===null||i===void 0?void 0:i.rootElement)||this.window}constructor(i,a){this.ownerDocument=null,this.globalContext=i,this.optionsArgs=a}}(r,n),this.actions=t.getActions(),this.monitor=t.getMonitor(),this.registry=t.getRegistry(),this.enterLeaveCounter=new G2(this.isNodeInDocument)}}const eC=function(e,t,r){return new J2(e,t,r)};var Gn;(function(e){e.mouse="mouse",e.touch="touch",e.keyboard="keyboard"})(Gn||(Gn={}));class tC{get delay(){var t;return(t=this.args.delay)!==null&&t!==void 0?t:0}get scrollAngleRanges(){return this.args.scrollAngleRanges}get getDropTargetElementsAtPoint(){return this.args.getDropTargetElementsAtPoint}get ignoreContextMenu(){var t;return(t=this.args.ignoreContextMenu)!==null&&t!==void 0&&t}get enableHoverOutsideTarget(){var t;return(t=this.args.enableHoverOutsideTarget)!==null&&t!==void 0&&t}get enableKeyboardEvents(){var t;return(t=this.args.enableKeyboardEvents)!==null&&t!==void 0&&t}get enableMouseEvents(){var t;return(t=this.args.enableMouseEvents)!==null&&t!==void 0&&t}get enableTouchEvents(){var t;return(t=this.args.enableTouchEvents)===null||t===void 0||t}get touchSlop(){return this.args.touchSlop||0}get delayTouchStart(){var t,r,n,i;return(i=(n=(t=this.args)===null||t===void 0?void 0:t.delayTouchStart)!==null&&n!==void 0?n:(r=this.args)===null||r===void 0?void 0:r.delay)!==null&&i!==void 0?i:0}get delayMouseStart(){var t,r,n,i;return(i=(n=(t=this.args)===null||t===void 0?void 0:t.delayMouseStart)!==null&&n!==void 0?n:(r=this.args)===null||r===void 0?void 0:r.delay)!==null&&i!==void 0?i:0}get window(){return this.context&&this.context.window?this.context.window:typeof window<"u"?window:void 0}get document(){var t;return!((t=this.context)===null||t===void 0)&&t.document?this.context.document:this.window?this.window.document:void 0}get rootElement(){var t;return((t=this.args)===null||t===void 0?void 0:t.rootElement)||this.document}constructor(t,r){this.args=t,this.context=r}}const rC=1,nC=0;function Eu(e){return e.button===void 0||e.button===nC}function c1(e){return!!e.targetTouches}function Go(e,t){return c1(e)?function(r,n){return r.targetTouches.length===1?Go(r.targetTouches[0]):n&&r.touches.length===1&&r.touches[0].target===n.target?Go(r.touches[0]):void 0}(e,t):{x:e.clientX,y:e.clientY}}const Qd=(()=>{let e=!1;try{addEventListener("test",()=>{},Object.defineProperty({},"passive",{get:()=>(e=!0,!0)}))}catch{}return e})(),Da={[Gn.mouse]:{start:"mousedown",move:"mousemove",end:"mouseup",contextmenu:"contextmenu"},[Gn.touch]:{start:"touchstart",move:"touchmove",end:"touchend"},[Gn.keyboard]:{keydown:"keydown"}};class Za{profile(){var t;return{sourceNodes:this.sourceNodes.size,sourcePreviewNodes:this.sourcePreviewNodes.size,sourcePreviewNodeOptions:this.sourcePreviewNodeOptions.size,targetNodes:this.targetNodes.size,dragOverTargetIds:((t=this.dragOverTargetIds)===null||t===void 0?void 0:t.length)||0}}get document(){return this.options.document}setup(){const t=this.options.rootElement;t&&(de(!Za.isSetUp,"Cannot have two Touch backends at the same time."),Za.isSetUp=!0,this.addEventListener(t,"start",this.getTopMoveStartHandler()),this.addEventListener(t,"start",this.handleTopMoveStartCapture,!0),this.addEventListener(t,"move",this.handleTopMove),this.addEventListener(t,"move",this.handleTopMoveCapture,!0),this.addEventListener(t,"end",this.handleTopMoveEndCapture,!0),this.options.enableMouseEvents&&!this.options.ignoreContextMenu&&this.addEventListener(t,"contextmenu",this.handleTopMoveEndCapture),this.options.enableKeyboardEvents&&this.addEventListener(t,"keydown",this.handleCancelOnEscape,!0))}teardown(){const t=this.options.rootElement;t&&(Za.isSetUp=!1,this._mouseClientOffset={},this.removeEventListener(t,"start",this.handleTopMoveStartCapture,!0),this.removeEventListener(t,"start",this.handleTopMoveStart),this.removeEventListener(t,"move",this.handleTopMoveCapture,!0),this.removeEventListener(t,"move",this.handleTopMove),this.removeEventListener(t,"end",this.handleTopMoveEndCapture,!0),this.options.enableMouseEvents&&!this.options.ignoreContextMenu&&this.removeEventListener(t,"contextmenu",this.handleTopMoveEndCapture),this.options.enableKeyboardEvents&&this.removeEventListener(t,"keydown",this.handleCancelOnEscape,!0),this.uninstallSourceNodeRemovalObserver())}addEventListener(t,r,n,i=!1){const a=Qd?{capture:i,passive:!1}:i;this.listenerTypes.forEach(function(s){const o=Da[s][r];o&&t.addEventListener(o,n,a)})}removeEventListener(t,r,n,i=!1){const a=Qd?{capture:i,passive:!1}:i;this.listenerTypes.forEach(function(s){const o=Da[s][r];o&&t.removeEventListener(o,n,a)})}connectDragSource(t,r){const n=this.handleMoveStart.bind(this,t);return this.sourceNodes.set(t,r),this.addEventListener(r,"start",n),()=>{this.sourceNodes.delete(t),this.removeEventListener(r,"start",n)}}connectDragPreview(t,r,n){return this.sourcePreviewNodeOptions.set(t,n),this.sourcePreviewNodes.set(t,r),()=>{this.sourcePreviewNodes.delete(t),this.sourcePreviewNodeOptions.delete(t)}}connectDropTarget(t,r){const n=this.options.rootElement;if(!this.document||!n)return()=>{};const i=a=>{if(!this.document||!n||!this.monitor.isDragging())return;let s;switch(a.type){case Da.mouse.move:s={x:a.clientX,y:a.clientY};break;case Da.touch.move:var o,l;s={x:((o=a.touches[0])===null||o===void 0?void 0:o.clientX)||0,y:((l=a.touches[0])===null||l===void 0?void 0:l.clientY)||0}}const u=s!=null?this.document.elementFromPoint(s.x,s.y):void 0,c=u&&r.contains(u);return u===r||c?this.handleMove(a,t):void 0};return this.addEventListener(this.document.body,"move",i),this.targetNodes.set(t,r),()=>{this.document&&(this.targetNodes.delete(t),this.removeEventListener(this.document.body,"move",i))}}getTopMoveStartHandler(){return this.options.delayTouchStart||this.options.delayMouseStart?this.handleTopMoveStartDelay:this.handleTopMoveStart}installSourceNodeRemovalObserver(t){this.uninstallSourceNodeRemovalObserver(),this.draggedSourceNode=t,this.draggedSourceNodeRemovalObserver=new MutationObserver(()=>{t&&!t.parentElement&&(this.resurrectSourceNode(),this.uninstallSourceNodeRemovalObserver())}),t&&t.parentElement&&this.draggedSourceNodeRemovalObserver.observe(t.parentElement,{childList:!0})}resurrectSourceNode(){this.document&&this.draggedSourceNode&&(this.draggedSourceNode.style.display="none",this.draggedSourceNode.removeAttribute("data-reactid"),this.document.body.appendChild(this.draggedSourceNode))}uninstallSourceNodeRemovalObserver(){this.draggedSourceNodeRemovalObserver&&this.draggedSourceNodeRemovalObserver.disconnect(),this.draggedSourceNodeRemovalObserver=void 0,this.draggedSourceNode=void 0}constructor(t,r,n){this.getSourceClientOffset=i=>{const a=this.sourceNodes.get(i);return a&&function(s){const o=s.nodeType===1?s:s.parentElement;if(!o)return;const{top:l,left:u}=o.getBoundingClientRect();return{x:u,y:l}}(a)},this.handleTopMoveStartCapture=i=>{Eu(i)&&(this.moveStartSourceIds=[])},this.handleMoveStart=i=>{Array.isArray(this.moveStartSourceIds)&&this.moveStartSourceIds.unshift(i)},this.handleTopMoveStart=i=>{if(!Eu(i))return;const a=Go(i);a&&(c1(i)&&(this.lastTargetTouchFallback=i.targetTouches[0]),this._mouseClientOffset=a),this.waitingForDelay=!1},this.handleTopMoveStartDelay=i=>{if(!Eu(i))return;const a=i.type===Da.touch.start?this.options.delayTouchStart:this.options.delayMouseStart;this.timeout=setTimeout(this.handleTopMoveStart.bind(this,i),a),this.waitingForDelay=!0},this.handleTopMoveCapture=()=>{this.dragOverTargetIds=[]},this.handleMove=(i,a)=>{this.dragOverTargetIds&&this.dragOverTargetIds.unshift(a)},this.handleTopMove=i=>{if(this.timeout&&clearTimeout(this.timeout),!this.document||this.waitingForDelay)return;const{moveStartSourceIds:a,dragOverTargetIds:s}=this,o=this.options.enableHoverOutsideTarget,l=Go(i,this.lastTargetTouchFallback);if(!l)return;if(this._isScrolling||!this.monitor.isDragging()&&function(m,S,B,R,L){if(!L)return!1;const M=180*Math.atan2(R-S,B-m)/Math.PI+180;for(let V=0;V=Q.start)&&(Q.end==null||M<=Q.end))return!0}return!1}(this._mouseClientOffset.x||0,this._mouseClientOffset.y||0,l.x,l.y,this.options.scrollAngleRanges))return void(this._isScrolling=!0);var u,c,f,d;if(!this.monitor.isDragging()&&this._mouseClientOffset.hasOwnProperty("x")&&a&&(u=this._mouseClientOffset.x||0,c=this._mouseClientOffset.y||0,f=l.x,d=l.y,Math.sqrt(Math.pow(Math.abs(f-u),2)+Math.pow(Math.abs(d-c),2))>(this.options.touchSlop?this.options.touchSlop:0))&&(this.moveStartSourceIds=void 0,this.actions.beginDrag(a,{clientOffset:this._mouseClientOffset,getSourceClientOffset:this.getSourceClientOffset,publishSource:!1})),!this.monitor.isDragging())return;const v=this.sourceNodes.get(this.monitor.getSourceId());this.installSourceNodeRemovalObserver(v),this.actions.publishDragSource(),i.cancelable&&i.preventDefault();const g=(s||[]).map(m=>this.targetNodes.get(m)).filter(m=>!!m),C=this.options.getDropTargetElementsAtPoint?this.options.getDropTargetElementsAtPoint(l.x,l.y,g):this.document.elementsFromPoint(l.x,l.y),I=[];for(const m in C){if(!C.hasOwnProperty(m))continue;let S=C[m];for(S!=null&&I.push(S);S;)S=S.parentElement,S&&I.indexOf(S)===-1&&I.push(S)}const y=I.filter(m=>g.indexOf(m)>-1).map(m=>this._getDropTargetId(m)).filter(m=>!!m).filter((m,S,B)=>B.indexOf(m)===S);if(o)for(const m in this.targetNodes){const S=this.targetNodes.get(m);if(v&&S&&S.contains(v)&&y.indexOf(m)===-1){y.unshift(m);break}}y.reverse(),this.actions.hover(y,{clientOffset:l})},this._getDropTargetId=i=>{const a=this.targetNodes.keys();let s=a.next();for(;s.done===!1;){const o=s.value;if(i===this.targetNodes.get(o))return o;s=a.next()}},this.handleTopMoveEndCapture=i=>{this._isScrolling=!1,this.lastTargetTouchFallback=void 0,function(a){return a.buttons===void 0||(a.buttons&rC)==0}(i)&&(this.monitor.isDragging()&&!this.monitor.didDrop()?(i.cancelable&&i.preventDefault(),this._mouseClientOffset={},this.uninstallSourceNodeRemovalObserver(),this.actions.drop(),this.actions.endDrag()):this.moveStartSourceIds=void 0)},this.handleCancelOnEscape=i=>{i.key==="Escape"&&this.monitor.isDragging()&&(this._mouseClientOffset={},this.uninstallSourceNodeRemovalObserver(),this.actions.endDrag())},this.options=new tC(n,r),this.actions=t.getActions(),this.monitor=t.getMonitor(),this.sourceNodes=new Map,this.sourcePreviewNodes=new Map,this.sourcePreviewNodeOptions=new Map,this.targetNodes=new Map,this.listenerTypes=[],this._mouseClientOffset={},this._isScrolling=!1,this.options.enableMouseEvents&&this.listenerTypes.push(Gn.mouse),this.options.enableTouchEvents&&this.listenerTypes.push(Gn.touch),this.options.enableKeyboardEvents&&this.listenerTypes.push(Gn.keyboard)}}const iC=function(e,t={},r={}){return new Za(e,t,r)},Es="abcdefgh".split(""),mo={a8:"bR",b8:"bN",c8:"bB",d8:"bQ",e8:"bK",f8:"bB",g8:"bN",h8:"bR",a7:"bP",b7:"bP",c7:"bP",d7:"bP",e7:"bP",f7:"bP",g7:"bP",h7:"bP",a2:"wP",b2:"wP",c2:"wP",d2:"wP",e2:"wP",f2:"wP",g2:"wP",h2:"wP",a1:"wR",b1:"wN",c1:"wB",d1:"wQ",e1:"wK",f1:"wB",g1:"wN",h1:"wR"},aC={a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7},sC={a:7,b:6,c:5,d:4,e:3,f:2,g:1,h:0},oC=[7,6,5,4,3,2,1,0],lC=[0,1,2,3,4,5,6,7],Wd={wP:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsx("path",{d:"m 22.5,9 c -2.21,0 -4,1.79 -4,4 0,0.89 0.29,1.71 0.78,2.38 C 17.33,16.5 16,18.59 16,21 c 0,2.03 0.94,3.84 2.41,5.03 C 15.41,27.09 11,31.58 11,39.5 H 34 C 34,31.58 29.59,27.09 26.59,26.03 28.06,24.84 29,23.03 29,21 29,18.59 27.67,16.5 25.72,15.38 26.21,14.71 26.5,13.89 26.5,13 c 0,-2.21 -1.79,-4 -4,-4 z",style:{opacity:"1",fill:"#ffffff",fillOpacity:"1",fillRule:"nonzero",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"miter",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}})})),wR:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{opacity:"1",fill:"#ffffff",fillOpacity:"1",fillRule:"evenodd",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsx("path",{d:"M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z ",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z ",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 34,14 L 31,17 L 14,17 L 11,14"}),D.jsx("path",{d:"M 31,17 L 31,29.5 L 14,29.5 L 14,17",style:{strokeLinecap:"butt",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5"}),D.jsx("path",{d:"M 11,14 L 34,14",style:{fill:"none",stroke:"#000000",strokeLinejoin:"miter"}})]}))})),wN:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{opacity:"1",fill:"none",fillOpacity:"1",fillRule:"evenodd",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsx("path",{d:"M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18",style:{fill:"#ffffff",stroke:"#000000"}}),D.jsx("path",{d:"M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10",style:{fill:"#ffffff",stroke:"#000000"}}),D.jsx("path",{d:"M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z",style:{fill:"#000000",stroke:"#000000"}}),D.jsx("path",{d:"M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z",transform:"matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)",style:{fill:"#000000",stroke:"#000000"}})]}))})),wB:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{opacity:"1",fill:"none",fillRule:"evenodd",fillOpacity:"1",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsxs("g",Object.assign({style:{fill:"#ffffff",stroke:"#000000",strokeLinecap:"butt"}},{children:[D.jsx("path",{d:"M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99 6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z"}),D.jsx("path",{d:"M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z"}),D.jsx("path",{d:"M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z"})]})),D.jsx("path",{d:"M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18",style:{fill:"none",stroke:"#000000",strokeLinejoin:"miter"}})]}))})),wQ:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{fill:"#ffffff",stroke:"#000000",strokeWidth:"1.5",strokeLinejoin:"round"}},{children:[D.jsx("path",{d:"M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z"}),D.jsx("path",{d:"M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 11,36 11,36 C 9.5,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z"}),D.jsx("path",{d:"M 11.5,30 C 15,29 30,29 33.5,30",style:{fill:"none"}}),D.jsx("path",{d:"M 12,33.5 C 18,32.5 27,32.5 33,33.5",style:{fill:"none"}}),D.jsx("circle",{cx:"6",cy:"12",r:"2"}),D.jsx("circle",{cx:"14",cy:"9",r:"2"}),D.jsx("circle",{cx:"22.5",cy:"8",r:"2"}),D.jsx("circle",{cx:"31",cy:"9",r:"2"}),D.jsx("circle",{cx:"39",cy:"12",r:"2"})]}))})),wK:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{fill:"none",fillOpacity:"1",fillRule:"evenodd",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsx("path",{d:"M 22.5,11.63 L 22.5,6",style:{fill:"none",stroke:"#000000",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 20,8 L 25,8",style:{fill:"none",stroke:"#000000",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25",style:{fill:"#ffffff",stroke:"#000000",strokeLinecap:"butt",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 12.5,37 C 18,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 20,16 10.5,13 6.5,19.5 C 3.5,25.5 12.5,30 12.5,30 L 12.5,37",style:{fill:"#ffffff",stroke:"#000000"}}),D.jsx("path",{d:"M 12.5,30 C 18,27 27,27 32.5,30",style:{fill:"none",stroke:"#000000"}}),D.jsx("path",{d:"M 12.5,33.5 C 18,30.5 27,30.5 32.5,33.5",style:{fill:"none",stroke:"#000000"}}),D.jsx("path",{d:"M 12.5,37 C 18,34 27,34 32.5,37",style:{fill:"none",stroke:"#000000"}})]}))})),bP:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsx("path",{d:"m 22.5,9 c -2.21,0 -4,1.79 -4,4 0,0.89 0.29,1.71 0.78,2.38 C 17.33,16.5 16,18.59 16,21 c 0,2.03 0.94,3.84 2.41,5.03 C 15.41,27.09 11,31.58 11,39.5 H 34 C 34,31.58 29.59,27.09 26.59,26.03 28.06,24.84 29,23.03 29,21 29,18.59 27.67,16.5 25.72,15.38 26.21,14.71 26.5,13.89 26.5,13 c 0,-2.21 -1.79,-4 -4,-4 z",style:{opacity:"1",fill:"#000000",fillOpacity:"1",fillRule:"nonzero",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"miter",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}})})),bR:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{opacity:"1",fill:"#000000",fillOpacity:"1",fillRule:"evenodd",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsx("path",{d:"M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z ",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z ",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z ",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z ",style:{strokeLinecap:"butt",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z ",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z ",style:{strokeLinecap:"butt"}}),D.jsx("path",{d:"M 12,35.5 L 33,35.5 L 33,35.5",style:{fill:"none",stroke:"#ffffff",strokeWidth:"1",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 13,31.5 L 32,31.5",style:{fill:"none",stroke:"#ffffff",strokeWidth:"1",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 14,29.5 L 31,29.5",style:{fill:"none",stroke:"#ffffff",strokeWidth:"1",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 14,16.5 L 31,16.5",style:{fill:"none",stroke:"#ffffff",strokeWidth:"1",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 11,14 L 34,14",style:{fill:"none",stroke:"#ffffff",strokeWidth:"1",strokeLinejoin:"miter"}})]}))})),bN:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{opacity:"1",fill:"none",fillOpacity:"1",fillRule:"evenodd",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsx("path",{d:"M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18",style:{fill:"#000000",stroke:"#000000"}}),D.jsx("path",{d:"M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10",style:{fill:"#000000",stroke:"#000000"}}),D.jsx("path",{d:"M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z",style:{fill:"#ffffff",stroke:"#ffffff"}}),D.jsx("path",{d:"M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z",transform:"matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)",style:{fill:"#ffffff",stroke:"#ffffff"}}),D.jsx("path",{d:"M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z ",style:{fill:"#ffffff",stroke:"none"}})]}))})),bB:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{opacity:"1",fill:"none",fillRule:"evenodd",fillOpacity:"1",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsxs("g",Object.assign({style:{fill:"#000000",stroke:"#000000",strokeLinecap:"butt"}},{children:[D.jsx("path",{d:"M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99 6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z"}),D.jsx("path",{d:"M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z"}),D.jsx("path",{d:"M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z"})]})),D.jsx("path",{d:"M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18",style:{fill:"none",stroke:"#ffffff",strokeLinejoin:"miter"}})]}))})),bQ:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{fill:"#000000",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"}},{children:[D.jsx("path",{d:"M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z",style:{strokeLinecap:"butt",fill:"#000000"}}),D.jsx("path",{d:"m 9,26 c 0,2 1.5,2 2.5,4 1,1.5 1,1 0.5,3.5 -1.5,1 -1,2.5 -1,2.5 -1.5,1.5 0,2.5 0,2.5 6.5,1 16.5,1 23,0 0,0 1.5,-1 0,-2.5 0,0 0.5,-1.5 -1,-2.5 -0.5,-2.5 -0.5,-2 0.5,-3.5 1,-2 2.5,-2 2.5,-4 -8.5,-1.5 -18.5,-1.5 -27,0 z"}),D.jsx("path",{d:"M 11.5,30 C 15,29 30,29 33.5,30"}),D.jsx("path",{d:"m 12,33.5 c 6,-1 15,-1 21,0"}),D.jsx("circle",{cx:"6",cy:"12",r:"2"}),D.jsx("circle",{cx:"14",cy:"9",r:"2"}),D.jsx("circle",{cx:"22.5",cy:"8",r:"2"}),D.jsx("circle",{cx:"31",cy:"9",r:"2"}),D.jsx("circle",{cx:"39",cy:"12",r:"2"}),D.jsx("path",{d:"M 11,38.5 A 35,35 1 0 0 34,38.5",style:{fill:"none",stroke:"#000000",strokeLinecap:"butt"}}),D.jsxs("g",Object.assign({style:{fill:"none",stroke:"#ffffff"}},{children:[D.jsx("path",{d:"M 11,29 A 35,35 1 0 1 34,29"}),D.jsx("path",{d:"M 12.5,31.5 L 32.5,31.5"}),D.jsx("path",{d:"M 11.5,34.5 A 35,35 1 0 0 33.5,34.5"}),D.jsx("path",{d:"M 10.5,37.5 A 35,35 1 0 0 34.5,37.5"})]}))]}))})),bK:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",width:"45",height:"45"},{children:D.jsxs("g",Object.assign({style:{fill:"none",fillOpacity:"1",fillRule:"evenodd",stroke:"#000000",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",strokeMiterlimit:"4",strokeDasharray:"none",strokeOpacity:"1"}},{children:[D.jsx("path",{d:"M 22.5,11.63 L 22.5,6",style:{fill:"none",stroke:"#000000",strokeLinejoin:"miter"},id:"path6570"}),D.jsx("path",{d:"M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25",style:{fill:"#000000",fillOpacity:"1",strokeLinecap:"butt",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 12.5,37 C 18,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 20,16 10.5,13 6.5,19.5 C 3.5,25.5 12.5,30 12.5,30 L 12.5,37",style:{fill:"#000000",stroke:"#000000"}}),D.jsx("path",{d:"M 20,8 L 25,8",style:{fill:"none",stroke:"#000000",strokeLinejoin:"miter"}}),D.jsx("path",{d:"M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.5,26.6 L 22.5,24.5 C 20,18 10.85,14 6.97,19.85 C 4.5,25.5 13,29.5 13,29.5",style:{fill:"none",stroke:"#ffffff"}}),D.jsx("path",{d:"M 12.5,30 C 18,27 27,27 32.5,30 M 12.5,33.5 C 18,30.5 27,30.5 32.5,33.5 M 12.5,37 C 18,34 27,34 32.5,37",style:{fill:"none",stroke:"#ffffff"}})]}))}))};function kc(e,t,r){const n=t/8,i=e==="white"?oC:lC;return{x:(e==="white"?aC:sC)[r[0]]*n+n/2,y:i[parseInt(r[1],10)-1]*n+n/2}}function Yd(e){let t=!1;return Object.keys(mo).forEach(r=>{e[r]!==mo[r]&&(t=!0)}),Object.keys(e).forEach(r=>{mo[r]!==e[r]&&(t=!0)}),t}function Xd(e){return e==="start"?mo:typeof e=="string"?function(t){if(!function(a){a=a.replace(/ .+$/,""),a=function(o){return o.replace(/8/g,"11111111").replace(/7/g,"1111111").replace(/6/g,"111111").replace(/5/g,"11111").replace(/4/g,"1111").replace(/3/g,"111").replace(/2/g,"11")}(a);const s=a.split("/");if(s.length!==8)return!1;for(let o=0;o<8;o++)if(s[o].length!==8||s[o].search(/[^kqrnbpKQRNBP1]/)!==-1)return!1;return!0}(t))return{};const r=(t=t.replace(/ .+$/,"")).split("/"),n={};let i=8;for(let a=0;a<8;a++){const s=r[a].split("");let o=0;for(let l=0;lq.useContext(f1),cC=q.forwardRef(({animationDuration:e=300,areArrowsAllowed:t=!0,arePiecesDraggable:r=!0,arePremovesAllowed:n=!1,boardOrientation:i="white",boardWidth:a,children:s,clearPremovesOnRightClick:o=!0,customArrows:l,customArrowColor:u="rgb(255,170,0)",customBoardStyle:c,customDarkSquareStyle:f={backgroundColor:"#B58863"},customDropSquareStyle:d={boxShadow:"inset 0 0 1px 6px rgba(255,255,255,0.75)"},customLightSquareStyle:v={backgroundColor:"#F0D9B5"},customPieces:g,customPremoveDarkSquareStyle:C={backgroundColor:"#A42323"},customPremoveLightSquareStyle:I={backgroundColor:"#BD2828"},customSquare:y="div",customSquareStyles:m,dropOffBoardAction:S="snapback",id:B=0,isDraggablePiece:R=()=>!0,getPositionObject:L=()=>{},onArrowsChange:M=()=>{},onDragOverSquare:V=()=>{},onMouseOutSquare:Q=()=>{},onMouseOverSquare:G=()=>{},onPieceClick:ce=()=>{},onPieceDragBegin:pe=()=>{},onPieceDragEnd:De=()=>{},onPieceDrop:Ue=()=>!0,onPromotionCheck:Ce=(ke,be,Qe)=>(Qe==="wP"&&ke[1]==="7"&&be[1]==="8"||Qe==="bP"&&ke[1]==="2"&&be[1]==="1")&&Math.abs(ke.charCodeAt(0)-be.charCodeAt(0))<=1,onPromotionPieceSelect:Le,onSquareClick:te=()=>{},onSquareRightClick:W=()=>{},position:ae="start",promotionDialogVariant:ne="default",promotionToSquare:ee=null,showBoardNotation:ie=!0,showPromotionDialog:Oe=!1,snapToCursor:se=!0,autoPromoteToQueen:Fe=!1},Re)=>{const[ke,be]=q.useState(Xd(ae)),[Qe,ct]=q.useState({removed:{},added:{}}),[ot,je]=q.useState(void 0),[We,Ke]=q.useState(Oe&&!Fe),[ht,Ns]=q.useState(null),[Ea,Sa]=q.useState(ee),[Ds,Ai]=q.useState([]),tr=q.useRef(Ds),[Gt,En]=q.useState(),[Ll,Kr]=q.useState(Object.assign(Object.assign({},Wd),g)),[Ol,w]=q.useState(!1),[h,p]=q.useState(),[_,N]=q.useState(!1);q.useImperativeHandle(Re,()=>({clearPremoves(Ye=!0){Ne(Ye)}})),q.useEffect(()=>{Kr(Object.assign(Object.assign({},Wd),g))},[g]),q.useEffect(()=>{Ke(Oe),Sa(ee)},[ee,Oe]),q.useEffect(()=>{var Ye,mt,At;Pl();const hr=Xd(ae),Dr=function(gr,Sn){const xa={removed:{},added:{}};return Object.keys(gr).forEach(rr=>{Sn[rr]!==gr[rr]&&(xa.removed[rr]=gr[rr])}),Object.keys(Sn).forEach(rr=>{gr[rr]!==Sn[rr]&&(xa.added[rr]=Sn[rr])}),xa}(ke,hr),pr=((Ye=Object.keys(Dr.added))===null||Ye===void 0?void 0:Ye.length)<=2?(At=(mt=Object.entries(Dr.added))===null||mt===void 0?void 0:mt[0])===null||At===void 0?void 0:At[1][0]:void 0;if(_)be(hr),N(!1),n&&He(pr),h&&clearTimeout(h);else if(Ol)be(hr),N(!1),n&&He(pr);else{Yd(hr)&&ot!==void 0?je(pr):Yd(hr)?je(void 0):je("b"),ct(Dr),N(!0);const gr=setTimeout(()=>{be(hr),N(!1),n&&He(pr)},e);p(gr)}return w(!1),L(hr),ge(),()=>{clearTimeout(h)}},[ae]);const{arrows:P,newArrow:F,clearArrows:ge,drawNewArrow:tt,onArrowDrawEnd:ze}=((Ye,mt=!0,At,hr)=>{const[Dr,pr]=q.useState([]),[gr,Sn]=q.useState([]),[xa,rr]=q.useState();q.useEffect(()=>{Array.isArray(Ye)&&pr(Ye==null?void 0:Ye.filter(nn=>nn[0]!==nn[1]))},[Ye]),q.useEffect(()=>{At==null||At(gr)},[gr]);const m0=[...gr,...Dr];return{arrows:m0,newArrow:xa,clearArrows:function(){Sn([]),rr(void 0)},drawNewArrow:(nn,Bi)=>{mt&&rr([nn,Bi,hr])},setArrows:Sn,onArrowDrawEnd:(nn,Bi)=>{if(nn===Bi)return;let C0;const cv=[nn,Bi,hr];C0=m0.every(([Ul,Ml])=>!(Ul===nn&&Ml===Bi))?[...gr,cv]:gr.filter(([Ul,Ml])=>!(Ul===nn&&Ml===Bi)),rr(void 0),Sn(C0)}}})(l,t,M,u);function He(Ye){if(tr.current.length===0)return;const mt=tr.current[0];if(mt.piece[0]!==void 0&&mt.piece[0]!==Ye&&Ue.length)if(je(mt.piece[0]),w(!0),Ue(mt.sourceSq,mt.targetSq,mt.piece)){const At=[...tr.current];At.shift(),tr.current=At,Ai([...At])}else Ne()}function Ne(Ye=!0){Ye&&je(void 0),tr.current=[],Ai([])}function Pl(){Ns(null),Sa(null),Ke(!1)}const uv={animationDuration:e,arePiecesDraggable:r,arePremovesAllowed:n,boardOrientation:i,boardWidth:a,customArrowColor:u,customBoardStyle:c,customDarkSquareStyle:f,customDropSquareStyle:d,customLightSquareStyle:v,customPremoveDarkSquareStyle:C,customPremoveLightSquareStyle:I,customSquare:y,customSquareStyles:m,id:B,isDraggablePiece:R,onDragOverSquare:V,onMouseOutSquare:Q,onMouseOverSquare:G,onPieceClick:ce,onPieceDragBegin:pe,onPieceDragEnd:De,onPieceDrop:Ue,onPromotionCheck:Ce,onPromotionPieceSelect:Le,onSquareClick:te,showBoardNotation:ie,snapToCursor:se,promotionDialogVariant:ne,arrows:P,newArrow:F,onArrowDrawEnd:ze,chessPieces:Ll,clearArrows:ge,drawNewArrow:tt,clearCurrentRightClickDown:function(){En(void 0)},currentPosition:ke,handleSetPosition:function(Ye,mt,At,hr){if(Ye===mt)return;if(ge(),n&&_||n&&(ot===At[0]||tr.current.filter(pr=>pr.piece[0]===At[0]).length>0)){const pr=[...tr.current];return pr.push({sourceSq:Ye,targetSq:mt,piece:At}),tr.current=pr,Ai([...pr]),void Pl()}if(!n&&_)return;const Dr=Object.assign({},ke);w(!!hr),je(At[0]),Ue.length?Ue(Ye,mt,At)||Ne():(S!=="trash"||mt||delete Dr[Ye],delete Dr[Ye],Dr[mt]=At,be(Dr)),Pl(),L(Dr)},isWaitingForAnimation:_,lastPieceColour:ot,onRightClickDown:function(Ye){En(Ye)},onRightClickUp:function(Ye){if(Gt){if(Gt===Ye)return En(void 0),o&&Ne(!1),void W(Ye)}else En(void 0)},positionDifferences:Qe,promoteFromSquare:ht,promoteToSquare:Ea,premoves:Ds,setPromoteFromSquare:Ns,setPromoteToSquare:Sa,setShowPromoteDialog:Ke,showPromoteDialog:We,autoPromoteToQueen:Fe,currentRightClickDown:Gt};return D.jsx(f1.Provider,Object.assign({value:uv},{children:s}))});function fC({row:e,col:t}){const{boardOrientation:r,boardWidth:n,customDarkSquareStyle:i,customLightSquareStyle:a}=Cn(),s=a.backgroundColor,o=i.backgroundColor,l=t===0,u=e===7;function c(){return r==="white"?8-e:e+1}function f(){return r==="black"?Es[7-t]:Es[t]}return l&&u?D.jsxs(D.Fragment,{children:[D.jsx("div",Object.assign({style:Object.assign(Object.assign({zIndex:3,position:"absolute"},{color:s}),Jd(n))},{children:c()})),D.jsx("div",Object.assign({style:Object.assign(Object.assign({zIndex:3,position:"absolute"},{color:s}),Zd(n))},{children:f()}))]}):u?D.jsx("div",Object.assign({style:Object.assign(Object.assign({userSelect:"none",zIndex:3,position:"absolute"},{color:t%2!=0?o:s}),Zd(n))},{children:f()})):l?D.jsx("div",Object.assign({style:Object.assign(Object.assign({userSelect:"none",zIndex:3,position:"absolute"},{color:e%2==0?o:s}),Jd(n))},{children:c()})):null}const Zd=e=>({alignSelf:"flex-end",paddingLeft:e/8-e/48,fontSize:e/48}),Jd=e=>({alignSelf:"flex-start",paddingRight:e/8-e/48,fontSize:e/48});function eh({isPremovedPiece:e=!1,piece:t,square:r,squares:n}){const{animationDuration:i,arePiecesDraggable:a,arePremovesAllowed:s,boardWidth:o,boardOrientation:l,chessPieces:u,currentPosition:c,id:f,isDraggablePiece:d,isWaitingForAnimation:v,onPieceClick:g,onPieceDragBegin:C,onPieceDragEnd:I,positionDifferences:y,premoves:m}=Cn(),[S,B]=q.useState({opacity:1,zIndex:5,touchAction:"none",cursor:a&&d({piece:t,sourceSquare:r})?"-webkit-grab":"default"}),[{canDrag:R,isDragging:L},M,V]=K2(()=>({type:"piece",item:()=>(C(t,r),{piece:t,square:r,id:f}),end:()=>I(t,r),collect:Q=>({canDrag:d({piece:t,sourceSquare:r}),isDragging:!!Q.isDragging()})}),[t,r,c,f]);return V((Js||(Js=new Image,Js.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="),Js),{captureDraggingState:!0}),q.useEffect(()=>{B(Q=>Object.assign(Object.assign({},Q),{opacity:L?0:1}))},[L]),q.useEffect(()=>{if(!s)return;let Q=!1;!e&&m.find(G=>G.targetSq===r)&&(Q=!0),m.find(G=>G.sourceSq===r&&G.piece===t)&&(Q=!0),B(G=>Object.assign(Object.assign({},G),{display:Q?"none":"unset"}))},[c,m]),q.useEffect(()=>{var Q;const G=(Q=y.removed)===null||Q===void 0?void 0:Q[r];if(!y.added)return;const ce=Object.entries(y.added).find(([pe,De])=>De===G||(G==null?void 0:G[1])==="P"&&(pe[1]==="1"||pe[1]==="8"));if(v&&G&&ce&&!e){const pe=r,De=ce[0];if(pe&&De){const Ue=o/8;B(Ce=>Object.assign(Object.assign({},Ce),{transform:`translate(${(l==="black"?-1:1)*(De.charCodeAt(0)-pe.charCodeAt(0))*Ue}px, ${(l==="black"?-1:1)*(Number(pe[1])-Number(De[1]))*Ue}px)`,transition:`transform ${i}ms`,zIndex:6}))}}},[y]),q.useEffect(()=>{const{sourceSq:Q}={sourceSq:n[r]};Q&&B(G=>Object.assign(Object.assign({},G),{transform:"translate(0px, 0px)",transition:"transform 0ms"}))},[c]),q.useEffect(()=>{B(Q=>Object.assign(Object.assign({},Q),{cursor:a&&d({piece:t,sourceSquare:r})?"-webkit-grab":"default"}))},[r,c,a]),D.jsx("div",Object.assign({ref:a&&R?M:null,onClick:()=>g(t),"data-piece":t,style:S},{children:typeof u[t]=="function"?u[t]({squareWidth:o/8,isDragging:L,square:r}):D.jsx("svg",Object.assign({viewBox:"1 1 43 43",width:o/8,height:o/8},{children:D.jsx("g",{children:u[t]})}))}))}function dC({square:e,squareColor:t,setSquares:r,squareHasPremove:n,children:i}){const a=q.useRef(null),{autoPromoteToQueen:s,boardWidth:o,boardOrientation:l,clearArrows:u,currentPosition:c,currentRightClickDown:f,customBoardStyle:d,customDarkSquareStyle:v,customDropSquareStyle:g,customLightSquareStyle:C,customPremoveDarkSquareStyle:I,customPremoveLightSquareStyle:y,customSquare:m,customSquareStyles:S,drawNewArrow:B,handleSetPosition:R,isWaitingForAnimation:L,lastPieceColour:M,onArrowDrawEnd:V,onDragOverSquare:Q,onMouseOutSquare:G,onMouseOverSquare:ce,onPieceDrop:pe,onPromotionCheck:De,onRightClickDown:Ue,onRightClickUp:Ce,onSquareClick:Le,setPromoteFromSquare:te,setPromoteToSquare:W,setShowPromoteDialog:ae}=Cn(),[{isOver:ne},ee]=$2(()=>({accept:"piece",drop:ie,collect:se=>({isOver:!!se.isOver()})}),[e,c,pe,L,M]);function ie(se){De(se.square,e,se.piece)?s?R(se.square,e,se.piece[0]==="w"?"wQ":"bQ"):(te(se.square),W(e),ae(!0)):R(se.square,e,se.piece,!0)}q.useEffect(()=>{if(a.current){const{x:se,y:Fe}=a.current.getBoundingClientRect();r(Re=>Object.assign(Object.assign({},Re),{[e]:{x:se,y:Fe}}))}},[o,l]);const Oe=Object.assign(Object.assign(Object.assign(Object.assign({},hC(e,l,d)),t==="black"?v:C),n&&(t==="black"?I:y)),ne&&g);return D.jsx("div",Object.assign({ref:ee,style:Oe,"data-square-color":t,"data-square":e,onMouseOver:se=>{se.buttons===2&&f&&B(f,e),se.relatedTarget&&se.currentTarget.contains(se.relatedTarget)||ce(e)},onMouseOut:se=>{se.relatedTarget&&se.currentTarget.contains(se.relatedTarget)||G(e)},onMouseDown:se=>{se.button===2&&Ue(e)},onMouseUp:se=>{se.button===2&&(f&&V(f,e),Ce(e))},onDragEnter:()=>Q(e),onClick:()=>{Le(e),u()},onContextMenu:se=>{se.preventDefault()}},{children:D.jsx(m,typeof m=="string"?Object.assign({ref:a,style:Object.assign(Object.assign(Object.assign({},rh(o)),th),!n&&(S==null?void 0:S[e]))},{children:i}):Object.assign({ref:a,square:e,squareColor:t,style:Object.assign(Object.assign(Object.assign({},rh(o)),th),!n&&(S==null?void 0:S[e]))},{children:i}))}))}const th={display:"flex",justifyContent:"center"},rh=e=>({width:e/8,height:e/8}),hC=(e,t,r)=>r!=null&&r.borderRadius?e==="a1"?t==="white"?{borderBottomLeftRadius:r.borderRadius}:{borderTopRightRadius:r.borderRadius}:e==="a8"?t==="white"?{borderTopLeftRadius:r.borderRadius}:{borderBottomRightRadius:r.borderRadius}:e==="h1"?t==="white"?{borderBottomRightRadius:r.borderRadius}:{borderTopLeftRadius:r.borderRadius}:e==="h8"?t==="white"?{borderTopRightRadius:r.borderRadius}:{borderBottomLeftRadius:r.borderRadius}:{}:{};function pC(){const[e,t]=q.useState({}),{boardOrientation:r,boardWidth:n,currentPosition:i,id:a,premoves:s,showBoardNotation:o}=Cn();return D.jsx("div",Object.assign({"data-boardid":a},{children:[...Array(8)].map((l,u)=>D.jsx("div",Object.assign({style:{display:"flex",flexWrap:"nowrap",width:n}},{children:[...Array(8)].map((c,f)=>{const d=r==="black"?Es[7-f]+(u+1):Es[f]+(8-u),v=f%2==u%2?"white":"black",g=s.find(I=>I.sourceSq===d||I.targetSq===d),C=s.find(I=>I.targetSq===d);return D.jsxs(dC,Object.assign({square:d,squareColor:v,setSquares:t,squareHasPremove:!!g},{children:[i[d]&&D.jsx(eh,{piece:i[d],square:d,squares:e}),C&&D.jsx(eh,{isPremovedPiece:!0,piece:C.piece,square:d,squares:e}),o&&D.jsx(fC,{row:u,col:f})]}),`${f}${u}`)})}),u.toString()))}))}const gC=()=>{const{arrows:e,newArrow:t,boardOrientation:r,boardWidth:n,customArrowColor:i}=Cn(),a=[...e,t].filter(Boolean);return D.jsx("svg",Object.assign({width:n,height:n,style:{position:"absolute",top:"0",left:"0",pointerEvents:"none",zIndex:"10"}},{children:a.map((s,o)=>{const[l,u,c]=s;if(l===u)return null;const f=kc(r,n,l),d=kc(r,n,u);let v=n/32;const g=o===e.length;e.some(S=>S[0]!==l&&S[1]===u)&&!g&&(v=n/16);const C=d.x-f.x,I=d.y-f.y,y=Math.hypot(I,C),m={x:f.x+C*(y-v)/y,y:f.y+I*(y-v)/y};return D.jsxs(q.Fragment,{children:[D.jsx("marker",Object.assign({id:`arrowhead-${o}`,markerWidth:"2",markerHeight:"2.5",refX:"1.25",refY:"1.25",orient:"auto"},{children:D.jsx("polygon",{points:"0.3 0, 2 1.25, 0.3 2.5",fill:c??i})})),D.jsx("line",{x1:f.x,y1:f.y,x2:m.x,y2:m.y,opacity:g?"0.5":"0.65",stroke:c??i,strokeWidth:g?.9*n/40:n/40,markerEnd:`url(#arrowhead-${o})`})]},`${l}-${u}${g?"-active":""}`)})}))};function vC({option:e}){const[t,r]=q.useState(!1),{boardWidth:n,chessPieces:i,customDarkSquareStyle:a,customLightSquareStyle:s,handleSetPosition:o,onPromotionPieceSelect:l,promoteFromSquare:u,promoteToSquare:c,promotionDialogVariant:f}=Cn(),d=()=>{switch(e[1]){case"Q":return a.backgroundColor;case"R":return s.backgroundColor;case"N":return f==="default"?s.backgroundColor:a.backgroundColor;case"B":return f==="default"?a.backgroundColor:s.backgroundColor}};return D.jsx("div",Object.assign({onClick:()=>{l!=null&&l.length?l(e):o(u,c,e,!0)},onMouseOver:()=>r(!0),onMouseOut:()=>r(!1),"data-piece":e,style:{cursor:"pointer",backgroundColor:t?d():`${d()}aa`,borderRadius:"4px",transition:"all 0.1s ease-out"}},{children:typeof i[e]=="function"?D.jsx("div",Object.assign({style:{transition:"all 0.1s ease-out",transform:t?"scale(1)":"scale(0.85)"}},{children:i[e]({squareWidth:n/8,isDragging:!1})})):D.jsx("svg",Object.assign({viewBox:"1 1 43 43",width:n/8,height:n/8,style:{transition:"all 0.1s ease-out",transform:t?"scale(1)":"scale(0.85)"}},{children:D.jsx("g",{children:i[e]})}))}))}function yC(){const{boardOrientation:e,boardWidth:t,promotionDialogVariant:r,promoteToSquare:n}=Cn(),i=(n==null?void 0:n[1])==="1"?"b":"w",a=[`${i??"w"}Q`,`${i??"w"}R`,`${i??"w"}N`,`${i??"w"}B`],s={default:{display:"grid",gridTemplateColumns:"1fr 1fr",transform:`translate(${-t/8}px, ${-t/8}px)`},vertical:{transform:`translate(${-t/16}px, ${-t/16}px)`},modal:{display:"flex",justifyContent:"center",alignItems:"center",transform:`translate(0px, ${3*t/8}px)`,width:"100%",height:t/4+"px",top:0,backgroundColor:"white",left:0}},o=kc(e,t,n||"a8");return D.jsx("div",Object.assign({style:Object.assign({position:"absolute",top:`${o==null?void 0:o.y}px`,left:`${o==null?void 0:o.x}px`,zIndex:1e3},s[r]),title:"Choose promotion piece"},{children:a.map(l=>D.jsx(vC,{option:l},l))}))}const mC={whiteKing:D.jsx("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",version:"1.1",style:{shapeRendering:"geometricPrecision",textRendering:"geometricPrecision",imageRendering:"crisp-edges"},viewBox:"0 0 4210 12970",x:"0px",y:"0px",fillRule:"evenodd",clipRule:"evenodd",width:"250",height:"250"},{children:D.jsx("g",{children:D.jsx("path",{style:{fill:"black",fillRule:"nonzero"},d:"M2105 0c169,0 286,160 249,315l200 0c-172,266 -231,479 -256,792 315,-24 530,-86 792,-255l0 897c-265,-171 -479,-231 -792,-256 18,234 75,495 185,682l339 0c233,0 369,269 225,456l545 0 -595 1916c130,94 158,275 59,402 465,0 416,568 51,568l-334 0 465 2867 332 0c250,0 381,306 199,485 162,63 273,220 273,399l0 633 168 0 0 475c-1403,0 -2807,0 -4210,0l0 -475 167 0 0 -633c0,-179 112,-336 274,-399 -181,-178 -52,-485 199,-485l332 0 465 -2867 -335 0c-353,0 -418,-568 51,-568 -98,-127 -70,-308 59,-402l-594 -1916c181,0 363,0 545,0 -144,-187 -9,-456 225,-456l339 0c110,-187 167,-448 185,-682 -315,25 -530,87 -793,256l0 -897c266,171 480,231 793,255 -25,-315 -87,-529 -256,-792l199 0c-36,-155 81,-315 250,-315zm-1994 10012l0 253 3988 0 0 -253c-1330,0 -2659,0 -3988,0zm484 -1060c-174,0 -316,142 -316,316l0 633 3652 0 0 -633c0,-174 -142,-316 -316,-316 -1007,0 -2013,0 -3020,0zm45 -457c-230,0 -225,345 0,345l2930 0c230,0 225,-345 0,-345 -977,0 -1953,0 -2930,0zm2020 -2978l-1111 0 -465 2867 2041 0 -465 -2867zm-1558 -456c-229,0 -224,345 0,345 669,0 1337,0 2005,0 230,0 225,-345 0,-345 -668,0 -1336,0 -2005,0zm1730 -457l-1454 0c-229,0 -224,345 0,345l1454 0c229,0 224,-345 0,-345zm-2064 -1862l544 1751c529,0 1057,0 1586,0l544 -1751c-892,0 -1783,0 -2674,0zm1085 -567l504 0c-126,-247 -163,-526 -177,-800 273,15 553,52 800,177l0 -504c-247,126 -527,163 -800,177 14,-273 51,-552 177,-799 -168,0 -336,0 -504,0 125,247 162,526 177,799 -274,-14 -553,-51 -800,-177l0 504c247,-125 527,-162 800,-177 -15,274 -52,553 -177,800zm969 111l-1434 0c-230,0 -225,345 0,345l1434 0c230,0 225,-345 0,-345zm-717 -2175c-105,0 -175,109 -133,204l266 0c42,-96 -30,-205 -133,-204z"})})}))};function CC({children:e}){try{return D.jsx(D.Fragment,{children:e})}catch(t){return console.log(t),D.jsx(d1,{showError:!0})}}function d1({showError:e=!1}){return D.jsxs("div",Object.assign({style:{display:"flex",justifyContent:"center",alignItems:"center",flexDirection:"column"}},{children:[D.jsx("div",Object.assign({style:{width:250,height:250,transform:"rotate(90deg)"}},{children:mC.whiteKing})),e&&D.jsx("h1",{children:"Something went wrong"})]}))}function EC(){const e=q.useRef(null),{boardWidth:t,clearCurrentRightClickDown:r,onPromotionPieceSelect:n,setShowPromoteDialog:i,showPromoteDialog:a,customBoardStyle:s}=Cn();return q.useEffect(()=>{function o(l){e.current&&!e.current.contains(l.target)&&r()}return document.addEventListener("mouseup",o),()=>{document.removeEventListener("mouseup",o)}},[]),t?D.jsx("div",Object.assign({style:{perspective:"1000px"}},{children:D.jsxs("div",Object.assign({ref:e,style:Object.assign(Object.assign({position:"relative"},SC(t)),s)},{children:[D.jsx(pC,{}),D.jsx(gC,{}),a&&D.jsxs(D.Fragment,{children:[D.jsx("div",{onClick:()=>{i(!1),n==null||n()},style:{position:"absolute",top:"0",left:"0",zIndex:"100",backgroundColor:"rgba(22,21,18,.7)",width:t,height:t}}),D.jsx(yC,{})]})]}))})):D.jsx(d1,{})}const SC=e=>({cursor:"default",height:e,width:e});function xC(){const{boardWidth:e,chessPieces:t,id:r,snapToCursor:n}=Cn(),i=function(c){const f=xi().getMonitor(),[d,v]=e1(f,c);return q.useEffect(()=>f.subscribeToOffsetChange(v)),q.useEffect(()=>f.subscribeToStateChange(v)),d}(c=>({item:c.getItem(),clientOffset:c.getClientOffset(),sourceClientOffset:c.getSourceClientOffset(),isDragging:c.isDragging()})),{isDragging:a,item:s,clientOffset:o,sourceClientOffset:l}=i,u=q.useCallback((c,f)=>{if(!c||!f)return{display:"none"};let{x:d,y:v}=n?c:f;if(n){const C=e/8/2;d-=C,v-=C}const g=`translate(${d}px, ${v}px)`;return{transform:g,WebkitTransform:g,touchAction:"none"}},[e,n]);return a&&s.id===r?D.jsx("div",Object.assign({style:{position:"fixed",pointerEvents:"none",zIndex:10,left:0,top:0}},{children:D.jsx("div",Object.assign({style:u(o,l)},{children:typeof t[s.piece]=="function"?t[s.piece]({squareWidth:e/8,isDragging:!0}):D.jsx("svg",Object.assign({viewBox:"1 1 43 43",width:e/8,height:e/8},{children:D.jsx("g",{children:t[s.piece]})}))}))})):null}const TC=q.forwardRef((e,t)=>{const{customDndBackend:r,customDndBackendOptions:n}=e,i=function(C,I){var y={};for(var m in C)Object.prototype.hasOwnProperty.call(C,m)&&I.indexOf(m)<0&&(y[m]=C[m]);if(C!=null&&typeof Object.getOwnPropertySymbols=="function"){var S=0;for(m=Object.getOwnPropertySymbols(C);S{c("ontouchstart"in window),l(!0),s(window)},[]),q.useEffect(()=>{var C;if(e.boardWidth===void 0&&(!((C=v.current)===null||C===void 0)&&C.offsetWidth)){const I=new ResizeObserver(()=>{var y;d((y=v.current)===null||y===void 0?void 0:y.offsetWidth)});return I.observe(v.current),()=>{I.disconnect()}}},[v.current,a]);const g=r||(u?iC:eC);return o&&a?D.jsx(CC,{children:D.jsxs("div",Object.assign({style:{display:"flex",flexDirection:"column",width:"100%"}},{children:[D.jsx("div",{ref:v,style:{width:"100%"}}),D.jsx(N2,Object.assign({backend:g,context:a,options:r?n:void 0},{children:f&&D.jsxs(cC,Object.assign({boardWidth:f},i,{ref:t},{children:[D.jsx(xC,{}),D.jsx(EC,{})]}))}))]}))}):null});var me={options:{usePureJavaScript:!1}},Zf={},wC=Zf,nh={};Zf.encode=function(e,t,r){if(typeof t!="string")throw new TypeError('"alphabet" must be a string.');if(r!==void 0&&typeof r!="number")throw new TypeError('"maxline" must be a number.');var n="";if(!(e instanceof Uint8Array))n=IC(e,t);else{var i=0,a=t.length,s=t.charAt(0),o=[0];for(i=0;i0;)o.push(u%a),u=u/a|0}for(i=0;e[i]===0&&i=0;--i)n+=t[o[i]]}if(r){var c=new RegExp(".{1,"+r+"}","g");n=n.match(c).join(`\r -`)}return n};Zf.decode=function(e,t){if(typeof e!="string")throw new TypeError('"input" must be a string.');if(typeof t!="string")throw new TypeError('"alphabet" must be a string.');var r=nh[t];if(!r){r=nh[t]=[];for(var n=0;n>=8;for(;u>0;)s.push(u&255),u>>=8}for(var c=0;e[c]===a&&c0;)a.push(o%n),o=o/n|0}var l="";for(r=0;e.at(r)===0&&r=0;--r)l+=t[a[r]];return l}var ih=me,ah=wC,b=ih.util=ih.util||{};(function(){if(typeof process<"u"&&process.nextTick&&!process.browser){b.nextTick=process.nextTick,typeof setImmediate=="function"?b.setImmediate=setImmediate:b.setImmediate=b.nextTick;return}if(typeof setImmediate=="function"){b.setImmediate=function(){return setImmediate.apply(void 0,arguments)},b.nextTick=function(o){return setImmediate(o)};return}if(b.setImmediate=function(o){setTimeout(o,0)},typeof window<"u"&&typeof window.postMessage=="function"){let o=function(l){if(l.source===window&&l.data===e){l.stopPropagation();var u=t.slice();t.length=0,u.forEach(function(c){c()})}};var s=o,e="forge.setImmediate",t=[];b.setImmediate=function(l){t.push(l),t.length===1&&window.postMessage(e,"*")},window.addEventListener("message",o,!0)}if(typeof MutationObserver<"u"){var r=Date.now(),n=!0,i=document.createElement("div"),t=[];new MutationObserver(function(){var l=t.slice();t.length=0,l.forEach(function(u){u()})}).observe(i,{attributes:!0});var a=b.setImmediate;b.setImmediate=function(l){Date.now()-r>15?(r=Date.now(),a(l)):(t.push(l),t.length===1&&i.setAttribute("a",n=!n))}}b.nextTick=b.setImmediate})();b.isNodejs=typeof process<"u"&&process.versions&&process.versions.node;b.globalScope=function(){return b.isNodejs?hv:typeof self>"u"?window:self}();b.isArray=Array.isArray||function(e){return Object.prototype.toString.call(e)==="[object Array]"};b.isArrayBuffer=function(e){return typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer};b.isArrayBufferView=function(e){return e&&b.isArrayBuffer(e.buffer)&&e.byteLength!==void 0};function Bs(e){if(!(e===8||e===16||e===24||e===32))throw new Error("Only 8, 16, 24, or 32 bits supported: "+e)}b.ByteBuffer=Jf;function Jf(e){if(this.data="",this.read=0,typeof e=="string")this.data=e;else if(b.isArrayBuffer(e)||b.isArrayBufferView(e))if(typeof Buffer<"u"&&e instanceof Buffer)this.data=e.toString("binary");else{var t=new Uint8Array(e);try{this.data=String.fromCharCode.apply(null,t)}catch{for(var r=0;r_C&&(this.data.substr(0,1),this._constructedStringLength=0)};b.ByteStringBuffer.prototype.length=function(){return this.data.length-this.read};b.ByteStringBuffer.prototype.isEmpty=function(){return this.length()<=0};b.ByteStringBuffer.prototype.putByte=function(e){return this.putBytes(String.fromCharCode(e))};b.ByteStringBuffer.prototype.fillWithByte=function(e,t){e=String.fromCharCode(e);for(var r=this.data;t>0;)t&1&&(r+=e),t>>>=1,t>0&&(e+=e);return this.data=r,this._optimizeConstructedString(t),this};b.ByteStringBuffer.prototype.putBytes=function(e){return this.data+=e,this._optimizeConstructedString(e.length),this};b.ByteStringBuffer.prototype.putString=function(e){return this.putBytes(b.encodeUtf8(e))};b.ByteStringBuffer.prototype.putInt16=function(e){return this.putBytes(String.fromCharCode(e>>8&255)+String.fromCharCode(e&255))};b.ByteStringBuffer.prototype.putInt24=function(e){return this.putBytes(String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e&255))};b.ByteStringBuffer.prototype.putInt32=function(e){return this.putBytes(String.fromCharCode(e>>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e&255))};b.ByteStringBuffer.prototype.putInt16Le=function(e){return this.putBytes(String.fromCharCode(e&255)+String.fromCharCode(e>>8&255))};b.ByteStringBuffer.prototype.putInt24Le=function(e){return this.putBytes(String.fromCharCode(e&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255))};b.ByteStringBuffer.prototype.putInt32Le=function(e){return this.putBytes(String.fromCharCode(e&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>24&255))};b.ByteStringBuffer.prototype.putInt=function(e,t){Bs(t);var r="";do t-=8,r+=String.fromCharCode(e>>t&255);while(t>0);return this.putBytes(r)};b.ByteStringBuffer.prototype.putSignedInt=function(e,t){return e<0&&(e+=2<0);return t};b.ByteStringBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<=r&&(t-=r<<1),t};b.ByteStringBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):e===0?t="":(t=this.read===0?this.data:this.data.slice(this.read),this.clear()),t};b.ByteStringBuffer.prototype.bytes=function(e){return typeof e>"u"?this.data.slice(this.read):this.data.slice(this.read,this.read+e)};b.ByteStringBuffer.prototype.at=function(e){return this.data.charCodeAt(this.read+e)};b.ByteStringBuffer.prototype.setAt=function(e,t){return this.data=this.data.substr(0,this.read+e)+String.fromCharCode(t)+this.data.substr(this.read+e+1),this};b.ByteStringBuffer.prototype.last=function(){return this.data.charCodeAt(this.data.length-1)};b.ByteStringBuffer.prototype.copy=function(){var e=b.createBuffer(this.data);return e.read=this.read,e};b.ByteStringBuffer.prototype.compact=function(){return this.read>0&&(this.data=this.data.slice(this.read),this.read=0),this};b.ByteStringBuffer.prototype.clear=function(){return this.data="",this.read=0,this};b.ByteStringBuffer.prototype.truncate=function(e){var t=Math.max(0,this.length()-e);return this.data=this.data.substr(this.read,t),this.read=0,this};b.ByteStringBuffer.prototype.toHex=function(){for(var e="",t=this.read;t=e)return this;t=Math.max(t||this.growSize,e);var r=new Uint8Array(this.data.buffer,this.data.byteOffset,this.data.byteLength),n=new Uint8Array(this.length()+t);return n.set(r),this.data=new DataView(n.buffer),this};b.DataBuffer.prototype.putByte=function(e){return this.accommodate(1),this.data.setUint8(this.write++,e),this};b.DataBuffer.prototype.fillWithByte=function(e,t){this.accommodate(t);for(var r=0;r>8&65535),this.data.setInt8(this.write,e>>16&255),this.write+=3,this};b.DataBuffer.prototype.putInt32=function(e){return this.accommodate(4),this.data.setInt32(this.write,e),this.write+=4,this};b.DataBuffer.prototype.putInt16Le=function(e){return this.accommodate(2),this.data.setInt16(this.write,e,!0),this.write+=2,this};b.DataBuffer.prototype.putInt24Le=function(e){return this.accommodate(3),this.data.setInt8(this.write,e>>16&255),this.data.setInt16(this.write,e>>8&65535,!0),this.write+=3,this};b.DataBuffer.prototype.putInt32Le=function(e){return this.accommodate(4),this.data.setInt32(this.write,e,!0),this.write+=4,this};b.DataBuffer.prototype.putInt=function(e,t){Bs(t),this.accommodate(t/8);do t-=8,this.data.setInt8(this.write++,e>>t&255);while(t>0);return this};b.DataBuffer.prototype.putSignedInt=function(e,t){return Bs(t),this.accommodate(t/8),e<0&&(e+=2<0);return t};b.DataBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<=r&&(t-=r<<1),t};b.DataBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):e===0?t="":(t=this.read===0?this.data:this.data.slice(this.read),this.clear()),t};b.DataBuffer.prototype.bytes=function(e){return typeof e>"u"?this.data.slice(this.read):this.data.slice(this.read,this.read+e)};b.DataBuffer.prototype.at=function(e){return this.data.getUint8(this.read+e)};b.DataBuffer.prototype.setAt=function(e,t){return this.data.setUint8(e,t),this};b.DataBuffer.prototype.last=function(){return this.data.getUint8(this.write-1)};b.DataBuffer.prototype.copy=function(){return new b.DataBuffer(this)};b.DataBuffer.prototype.compact=function(){if(this.read>0){var e=new Uint8Array(this.data.buffer,this.read),t=new Uint8Array(e.byteLength);t.set(e),this.data=new DataView(t),this.write-=this.read,this.read=0}return this};b.DataBuffer.prototype.clear=function(){return this.data=new DataView(new ArrayBuffer(0)),this.read=this.write=0,this};b.DataBuffer.prototype.truncate=function(e){return this.write=Math.max(0,this.length()-e),this.read=Math.min(this.read,this.write),this};b.DataBuffer.prototype.toHex=function(){for(var e="",t=this.read;t0;)t&1&&(r+=e),t>>>=1,t>0&&(e+=e);return r};b.xorBytes=function(e,t,r){for(var n="",i="",a="",s=0,o=0;r>0;--r,++s)i=e.charCodeAt(s)^t.charCodeAt(s),o>=10&&(n+=a,a="",o=0),a+=String.fromCharCode(i),++o;return n+=a,n};b.hexToBytes=function(e){var t="",r=0;for(e.length&!0&&(r=1,t+=String.fromCharCode(parseInt(e[0],16)));r>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e&255)};var Ln="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",On=[62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],h1="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";b.encode64=function(e,t){for(var r="",n="",i,a,s,o=0;o>2),r+=Ln.charAt((i&3)<<4|a>>4),isNaN(a)?r+="==":(r+=Ln.charAt((a&15)<<2|s>>6),r+=isNaN(s)?"=":Ln.charAt(s&63)),t&&r.length>t&&(n+=r.substr(0,t)+`\r -`,r=r.substr(t));return n+=r,n};b.decode64=function(e){e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var t="",r,n,i,a,s=0;s>4),i!==64&&(t+=String.fromCharCode((n&15)<<4|i>>2),a!==64&&(t+=String.fromCharCode((i&3)<<6|a)));return t};b.encodeUtf8=function(e){return unescape(encodeURIComponent(e))};b.decodeUtf8=function(e){return decodeURIComponent(escape(e))};b.binary={raw:{},hex:{},base64:{},base58:{},baseN:{encode:ah.encode,decode:ah.decode}};b.binary.raw.encode=function(e){return String.fromCharCode.apply(null,e)};b.binary.raw.decode=function(e,t,r){var n=t;n||(n=new Uint8Array(e.length)),r=r||0;for(var i=r,a=0;a>2),r+=Ln.charAt((i&3)<<4|a>>4),isNaN(a)?r+="==":(r+=Ln.charAt((a&15)<<2|s>>6),r+=isNaN(s)?"=":Ln.charAt(s&63)),t&&r.length>t&&(n+=r.substr(0,t)+`\r -`,r=r.substr(t));return n+=r,n};b.binary.base64.decode=function(e,t,r){var n=t;n||(n=new Uint8Array(Math.ceil(e.length/4)*3)),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,""),r=r||0;for(var i,a,s,o,l=0,u=r;l>4,s!==64&&(n[u++]=(a&15)<<4|s>>2,o!==64&&(n[u++]=(s&3)<<6|o));return t?u-r:n.subarray(0,u)};b.binary.base58.encode=function(e,t){return b.binary.baseN.encode(e,h1,t)};b.binary.base58.decode=function(e,t){return b.binary.baseN.decode(e,h1,t)};b.text={utf8:{},utf16:{}};b.text.utf8.encode=function(e,t,r){e=b.encodeUtf8(e);var n=t;n||(n=new Uint8Array(e.length)),r=r||0;for(var i=r,a=0;a"u"&&(r=["web","flash"]);var i,a=!1,s=null;for(var o in r){i=r[o];try{if(i==="flash"||i==="both"){if(t[0]===null)throw new Error("Flash local storage not available.");n=e.apply(this,t),a=i==="flash"}(i==="web"||i==="both")&&(t[0]=localStorage,n=e.apply(this,t),a=!0)}catch(l){s=l}if(a)break}if(!a)throw s;return n};b.setItem=function(e,t,r,n,i){xl(BC,arguments,i)};b.getItem=function(e,t,r,n){return xl(kC,arguments,n)};b.removeItem=function(e,t,r,n){xl(bC,arguments,n)};b.clearItems=function(e,t,r){xl(NC,arguments,r)};b.isEmpty=function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0};b.format=function(e){for(var t=/%./g,r,n,i=0,a=[],s=0;r=t.exec(e);){n=e.substring(s,t.lastIndex-2),n.length>0&&a.push(n),s=t.lastIndex;var o=r[0][1];switch(o){case"s":case"o":i");break;case"%":a.push("%");break;default:a.push("<%"+o+"?>")}}return a.push(e.substring(s)),a.join("")};b.formatNumber=function(e,t,r,n){var i=e,a=isNaN(t=Math.abs(t))?2:t,s=r===void 0?",":r,o=n===void 0?".":n,l=i<0?"-":"",u=parseInt(i=Math.abs(+i||0).toFixed(a),10)+"",c=u.length>3?u.length%3:0;return l+(c?u.substr(0,c)+o:"")+u.substr(c).replace(/(\d{3})(?=\d)/g,"$1"+o)+(a?s+Math.abs(i-u).toFixed(a).slice(2):"")};b.formatSize=function(e){return e>=1073741824?e=b.formatNumber(e/1073741824,2,".","")+" GiB":e>=1048576?e=b.formatNumber(e/1048576,2,".","")+" MiB":e>=1024?e=b.formatNumber(e/1024,0)+" KiB":e=b.formatNumber(e,0)+" bytes",e};b.bytesFromIP=function(e){return e.indexOf(".")!==-1?b.bytesFromIPv4(e):e.indexOf(":")!==-1?b.bytesFromIPv6(e):null};b.bytesFromIPv4=function(e){if(e=e.split("."),e.length!==4)return null;for(var t=b.createBuffer(),r=0;rr[n].end-r[n].start&&(n=r.length-1))}t.push(a)}if(r.length>0){var l=r[n];l.end-l.start>0&&(t.splice(l.start,l.end-l.start+1,""),l.start===0&&t.unshift(""),l.end===7&&t.push(""))}return t.join(":")};b.estimateCores=function(e,t){if(typeof e=="function"&&(t=e,e={}),e=e||{},"cores"in b&&!e.update)return t(null,b.cores);if(typeof navigator<"u"&&"hardwareConcurrency"in navigator&&navigator.hardwareConcurrency>0)return b.cores=navigator.hardwareConcurrency,t(null,b.cores);if(typeof Worker>"u")return b.cores=1,t(null,b.cores);if(typeof Blob>"u")return b.cores=2,t(null,b.cores);var r=URL.createObjectURL(new Blob(["(",(function(){self.addEventListener("message",function(s){var o=Date.now(),l=o+4;self.postMessage({st:o,et:l})})}).toString(),")()"],{type:"application/javascript"}));n([],5,16);function n(s,o,l){if(o===0){var u=Math.floor(s.reduce(function(c,f){return c+f},0)/s.length);return b.cores=Math.max(1,u),URL.revokeObjectURL(r),t(null,b.cores)}i(l,function(c,f){s.push(a(l,f)),n(s,o-1,l)})}function i(s,o){for(var l=[],u=[],c=0;cv.st&&c.stc.st&&v.st0))return!0;for(var n=0;n0))return!0;for(var n=0;n0)return!1;var r=e.length(),n=e.at(r-1);return n>this.blockSize<<2?!1:(e.truncate(n),!0)};xe.cbc=function(e){e=e||{},this.name="CBC",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints)};xe.cbc.prototype.start=function(e){if(e.iv===null){if(!this._prev)throw new Error("Invalid IV parameter.");this._iv=this._prev.slice(0)}else if("iv"in e)this._iv=Tl(e.iv,this.blockSize),this._prev=this._iv.slice(0);else throw new Error("Invalid IV parameter.")};xe.cbc.prototype.encrypt=function(e,t,r){if(e.length()0))return!0;for(var n=0;n0))return!0;for(var n=0;n0)return!1;var r=e.length(),n=e.at(r-1);return n>this.blockSize<<2?!1:(e.truncate(n),!0)};xe.cfb=function(e){e=e||{},this.name="CFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialBlock=new Array(this._ints),this._partialOutput=wt.util.createBuffer(),this._partialBytes=0};xe.cfb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=Tl(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0};xe.cfb.prototype.encrypt=function(e,t,r){var n=e.length();if(n===0)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),this._partialBytes===0&&n>=this.blockSize){for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(var i=0;i0)e.read-=this.blockSize;else for(var i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!r)return t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0};xe.cfb.prototype.decrypt=function(e,t,r){var n=e.length();if(n===0)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),this._partialBytes===0&&n>=this.blockSize){for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(var i=0;i0)e.read-=this.blockSize;else for(var i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!r)return t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0};xe.ofb=function(e){e=e||{},this.name="OFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=wt.util.createBuffer(),this._partialBytes=0};xe.ofb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=Tl(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0};xe.ofb.prototype.encrypt=function(e,t,r){var n=e.length();if(e.length()===0)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),this._partialBytes===0&&n>=this.blockSize){for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(var i=0;i0)e.read-=this.blockSize;else for(var i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!r)return t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0};xe.ofb.prototype.decrypt=xe.ofb.prototype.encrypt;xe.ctr=function(e){e=e||{},this.name="CTR",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=wt.util.createBuffer(),this._partialBytes=0};xe.ctr.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=Tl(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0};xe.ctr.prototype.encrypt=function(e,t,r){var n=e.length();if(n===0)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),this._partialBytes===0&&n>=this.blockSize)for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(var i=0;i0&&(e.read-=this.blockSize),this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!r)return t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0}wl(this._inBlock)};xe.ctr.prototype.decrypt=xe.ctr.prototype.encrypt;xe.gcm=function(e){e=e||{},this.name="GCM",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints),this._partialOutput=wt.util.createBuffer(),this._partialBytes=0,this._R=3774873600};xe.gcm.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");var t=wt.util.createBuffer(e.iv);this._cipherLength=0;var r;if("additionalData"in e?r=wt.util.createBuffer(e.additionalData):r=wt.util.createBuffer(),"tagLength"in e?this._tagLength=e.tagLength:this._tagLength=128,this._tag=null,e.decrypt&&(this._tag=wt.util.createBuffer(e.tag).getBytes(),this._tag.length!==this._tagLength/8))throw new Error("Authentication tag does not match tag length.");this._hashBlock=new Array(this._ints),this.tag=null,this._hashSubkey=new Array(this._ints),this.cipher.encrypt([0,0,0,0],this._hashSubkey),this.componentBits=4,this._m=this.generateHashTable(this._hashSubkey,this.componentBits);var n=t.length();if(n===12)this._j0=[t.getInt32(),t.getInt32(),t.getInt32(),1];else{for(this._j0=[0,0,0,0];t.length()>0;)this._j0=this.ghash(this._hashSubkey,this._j0,[t.getInt32(),t.getInt32(),t.getInt32(),t.getInt32()]);this._j0=this.ghash(this._hashSubkey,this._j0,[0,0].concat(bc(n*8)))}this._inBlock=this._j0.slice(0),wl(this._inBlock),this._partialBytes=0,r=wt.util.createBuffer(r),this._aDataLength=bc(r.length()*8);var i=r.length()%this.blockSize;for(i&&r.fillWithByte(0,this.blockSize-i),this._s=[0,0,0,0];r.length()>0;)this._s=this.ghash(this._hashSubkey,this._s,[r.getInt32(),r.getInt32(),r.getInt32(),r.getInt32()])};xe.gcm.prototype.encrypt=function(e,t,r){var n=e.length();if(n===0)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),this._partialBytes===0&&n>=this.blockSize){for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(var i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!r)return e.read-=this.blockSize,t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;t.putBytes(this._partialOutput.getBytes(n-this._partialBytes)),this._partialBytes=0}this._s=this.ghash(this._hashSubkey,this._s,this._outBlock),wl(this._inBlock)};xe.gcm.prototype.decrypt=function(e,t,r){var n=e.length();if(n0))return!0;this.cipher.encrypt(this._inBlock,this._outBlock),wl(this._inBlock),this._hashBlock[0]=e.getInt32(),this._hashBlock[1]=e.getInt32(),this._hashBlock[2]=e.getInt32(),this._hashBlock[3]=e.getInt32(),this._s=this.ghash(this._hashSubkey,this._s,this._hashBlock);for(var i=0;i0;--n)t[n]=e[n]>>>1|(e[n-1]&1)<<31;t[0]=e[0]>>>1,r&&(t[0]^=this._R)};xe.gcm.prototype.tableMultiply=function(e){for(var t=[0,0,0,0],r=0;r<32;++r){var n=r/8|0,i=e[n]>>>(7-r%8)*4&15,a=this._m[r][i];t[0]^=a[0],t[1]^=a[1],t[2]^=a[2],t[3]^=a[3]}return t};xe.gcm.prototype.ghash=function(e,t,r){return t[0]^=r[0],t[1]^=r[1],t[2]^=r[2],t[3]^=r[3],this.tableMultiply(t)};xe.gcm.prototype.generateHashTable=function(e,t){for(var r=8/t,n=4*r,i=16*r,a=new Array(i),s=0;s>>1,i=new Array(r);i[n]=e.slice(0);for(var a=n>>>1;a>0;)this.pow(i[2*a],i[a]=[]),a>>=1;for(a=2;a4){var r=e;e=wt.util.createBuffer();for(var n=0;n>>2;for(var n=0;n>8^o&255^99,Ft[r]=o,Nc[o]=r,l=e[o],i=e[r],a=e[i],s=e[a],u=l<<24^o<<16^o<<8^(o^l),c=(i^a^s)<<24^(r^s)<<16^(r^a^s)<<8^(r^i^s);for(var f=0;f<4;++f)fi[f][r]=u,Mr[f][o]=c,u=u<<24|u>>>8,c=c<<24|c>>>8;r===0?r=n=1:(r=i^e[e[e[i^s]]],n^=e[e[n]])}}function v1(e,t){for(var r=e.slice(0),n,i=1,a=r.length,s=a+6+1,o=Ni*s,l=a;l>>16&255]<<24^Ft[n>>>8&255]<<16^Ft[n&255]<<8^Ft[n>>>24]^p1[i]<<24,i++):a>6&&l%a===4&&(n=Ft[n>>>24]<<24^Ft[n>>>16&255]<<16^Ft[n>>>8&255]<<8^Ft[n&255]),r[l]=r[l-a]^n;if(t){var u,c=Mr[0],f=Mr[1],d=Mr[2],v=Mr[3],g=r.slice(0);o=r.length;for(var l=0,C=o-Ni;l>>24]]^f[Ft[u>>>16&255]]^d[Ft[u>>>8&255]]^v[Ft[u&255]];r=g}return r}function Dc(e,t,r,n){var i=e.length/4-1,a,s,o,l,u;n?(a=Mr[0],s=Mr[1],o=Mr[2],l=Mr[3],u=Nc):(a=fi[0],s=fi[1],o=fi[2],l=fi[3],u=Ft);var c,f,d,v,g,C,I;c=t[0]^e[0],f=t[n?3:1]^e[1],d=t[2]^e[2],v=t[n?1:3]^e[3];for(var y=3,m=1;m>>24]^s[f>>>16&255]^o[d>>>8&255]^l[v&255]^e[++y],C=a[f>>>24]^s[d>>>16&255]^o[v>>>8&255]^l[c&255]^e[++y],I=a[d>>>24]^s[v>>>16&255]^o[c>>>8&255]^l[f&255]^e[++y],v=a[v>>>24]^s[c>>>16&255]^o[f>>>8&255]^l[d&255]^e[++y],c=g,f=C,d=I;r[0]=u[c>>>24]<<24^u[f>>>16&255]<<16^u[d>>>8&255]<<8^u[v&255]^e[++y],r[n?3:1]=u[f>>>24]<<24^u[d>>>16&255]<<16^u[v>>>8&255]<<8^u[c&255]^e[++y],r[2]=u[d>>>24]<<24^u[v>>>16&255]<<16^u[c>>>8&255]<<8^u[f&255]^e[++y],r[n?1:3]=u[v>>>24]<<24^u[c>>>16&255]<<16^u[f>>>8&255]<<8^u[d&255]^e[++y]}function Il(e){e=e||{};var t=(e.mode||"CBC").toUpperCase(),r="AES-"+t,n;e.decrypt?n=$e.cipher.createDecipher(r,e.key):n=$e.cipher.createCipher(r,e.key);var i=n.start;return n.start=function(a,s){var o=null;s instanceof $e.util.ByteBuffer&&(o=s,s={}),s=s||{},s.output=o,s.iv=a,i.call(n,s)},n}var Ja=me;Ja.pki=Ja.pki||{};var Rc=Ja.pki.oids=Ja.oids=Ja.oids||{};function z(e,t){Rc[e]=t,Rc[t]=e}function Pe(e,t){Rc[e]=t}z("1.2.840.113549.1.1.1","rsaEncryption");z("1.2.840.113549.1.1.4","md5WithRSAEncryption");z("1.2.840.113549.1.1.5","sha1WithRSAEncryption");z("1.2.840.113549.1.1.7","RSAES-OAEP");z("1.2.840.113549.1.1.8","mgf1");z("1.2.840.113549.1.1.9","pSpecified");z("1.2.840.113549.1.1.10","RSASSA-PSS");z("1.2.840.113549.1.1.11","sha256WithRSAEncryption");z("1.2.840.113549.1.1.12","sha384WithRSAEncryption");z("1.2.840.113549.1.1.13","sha512WithRSAEncryption");z("1.3.101.112","EdDSA25519");z("1.2.840.10040.4.3","dsa-with-sha1");z("1.3.14.3.2.7","desCBC");z("1.3.14.3.2.26","sha1");z("1.3.14.3.2.29","sha1WithRSASignature");z("2.16.840.1.101.3.4.2.1","sha256");z("2.16.840.1.101.3.4.2.2","sha384");z("2.16.840.1.101.3.4.2.3","sha512");z("2.16.840.1.101.3.4.2.4","sha224");z("2.16.840.1.101.3.4.2.5","sha512-224");z("2.16.840.1.101.3.4.2.6","sha512-256");z("1.2.840.113549.2.2","md2");z("1.2.840.113549.2.5","md5");z("1.2.840.113549.1.7.1","data");z("1.2.840.113549.1.7.2","signedData");z("1.2.840.113549.1.7.3","envelopedData");z("1.2.840.113549.1.7.4","signedAndEnvelopedData");z("1.2.840.113549.1.7.5","digestedData");z("1.2.840.113549.1.7.6","encryptedData");z("1.2.840.113549.1.9.1","emailAddress");z("1.2.840.113549.1.9.2","unstructuredName");z("1.2.840.113549.1.9.3","contentType");z("1.2.840.113549.1.9.4","messageDigest");z("1.2.840.113549.1.9.5","signingTime");z("1.2.840.113549.1.9.6","counterSignature");z("1.2.840.113549.1.9.7","challengePassword");z("1.2.840.113549.1.9.8","unstructuredAddress");z("1.2.840.113549.1.9.14","extensionRequest");z("1.2.840.113549.1.9.20","friendlyName");z("1.2.840.113549.1.9.21","localKeyId");z("1.2.840.113549.1.9.22.1","x509Certificate");z("1.2.840.113549.1.12.10.1.1","keyBag");z("1.2.840.113549.1.12.10.1.2","pkcs8ShroudedKeyBag");z("1.2.840.113549.1.12.10.1.3","certBag");z("1.2.840.113549.1.12.10.1.4","crlBag");z("1.2.840.113549.1.12.10.1.5","secretBag");z("1.2.840.113549.1.12.10.1.6","safeContentsBag");z("1.2.840.113549.1.5.13","pkcs5PBES2");z("1.2.840.113549.1.5.12","pkcs5PBKDF2");z("1.2.840.113549.1.12.1.1","pbeWithSHAAnd128BitRC4");z("1.2.840.113549.1.12.1.2","pbeWithSHAAnd40BitRC4");z("1.2.840.113549.1.12.1.3","pbeWithSHAAnd3-KeyTripleDES-CBC");z("1.2.840.113549.1.12.1.4","pbeWithSHAAnd2-KeyTripleDES-CBC");z("1.2.840.113549.1.12.1.5","pbeWithSHAAnd128BitRC2-CBC");z("1.2.840.113549.1.12.1.6","pbewithSHAAnd40BitRC2-CBC");z("1.2.840.113549.2.7","hmacWithSHA1");z("1.2.840.113549.2.8","hmacWithSHA224");z("1.2.840.113549.2.9","hmacWithSHA256");z("1.2.840.113549.2.10","hmacWithSHA384");z("1.2.840.113549.2.11","hmacWithSHA512");z("1.2.840.113549.3.7","des-EDE3-CBC");z("2.16.840.1.101.3.4.1.2","aes128-CBC");z("2.16.840.1.101.3.4.1.22","aes192-CBC");z("2.16.840.1.101.3.4.1.42","aes256-CBC");z("2.5.4.3","commonName");z("2.5.4.4","surname");z("2.5.4.5","serialNumber");z("2.5.4.6","countryName");z("2.5.4.7","localityName");z("2.5.4.8","stateOrProvinceName");z("2.5.4.9","streetAddress");z("2.5.4.10","organizationName");z("2.5.4.11","organizationalUnitName");z("2.5.4.12","title");z("2.5.4.13","description");z("2.5.4.15","businessCategory");z("2.5.4.17","postalCode");z("2.5.4.42","givenName");z("1.3.6.1.4.1.311.60.2.1.2","jurisdictionOfIncorporationStateOrProvinceName");z("1.3.6.1.4.1.311.60.2.1.3","jurisdictionOfIncorporationCountryName");z("2.16.840.1.113730.1.1","nsCertType");z("2.16.840.1.113730.1.13","nsComment");Pe("2.5.29.1","authorityKeyIdentifier");Pe("2.5.29.2","keyAttributes");Pe("2.5.29.3","certificatePolicies");Pe("2.5.29.4","keyUsageRestriction");Pe("2.5.29.5","policyMapping");Pe("2.5.29.6","subtreesConstraint");Pe("2.5.29.7","subjectAltName");Pe("2.5.29.8","issuerAltName");Pe("2.5.29.9","subjectDirectoryAttributes");Pe("2.5.29.10","basicConstraints");Pe("2.5.29.11","nameConstraints");Pe("2.5.29.12","policyConstraints");Pe("2.5.29.13","basicConstraints");z("2.5.29.14","subjectKeyIdentifier");z("2.5.29.15","keyUsage");Pe("2.5.29.16","privateKeyUsagePeriod");z("2.5.29.17","subjectAltName");z("2.5.29.18","issuerAltName");z("2.5.29.19","basicConstraints");Pe("2.5.29.20","cRLNumber");Pe("2.5.29.21","cRLReason");Pe("2.5.29.22","expirationDate");Pe("2.5.29.23","instructionCode");Pe("2.5.29.24","invalidityDate");Pe("2.5.29.25","cRLDistributionPoints");Pe("2.5.29.26","issuingDistributionPoint");Pe("2.5.29.27","deltaCRLIndicator");Pe("2.5.29.28","issuingDistributionPoint");Pe("2.5.29.29","certificateIssuer");Pe("2.5.29.30","nameConstraints");z("2.5.29.31","cRLDistributionPoints");z("2.5.29.32","certificatePolicies");Pe("2.5.29.33","policyMappings");Pe("2.5.29.34","policyConstraints");z("2.5.29.35","authorityKeyIdentifier");Pe("2.5.29.36","policyConstraints");z("2.5.29.37","extKeyUsage");Pe("2.5.29.46","freshestCRL");Pe("2.5.29.54","inhibitAnyPolicy");z("1.3.6.1.4.1.11129.2.4.2","timestampList");z("1.3.6.1.5.5.7.1.1","authorityInfoAccess");z("1.3.6.1.5.5.7.3.1","serverAuth");z("1.3.6.1.5.5.7.3.2","clientAuth");z("1.3.6.1.5.5.7.3.3","codeSigning");z("1.3.6.1.5.5.7.3.4","emailProtection");z("1.3.6.1.5.5.7.3.8","timeStamping");var Ze=me,X=Ze.asn1=Ze.asn1||{};X.Class={UNIVERSAL:0,APPLICATION:64,CONTEXT_SPECIFIC:128,PRIVATE:192};X.Type={NONE:0,BOOLEAN:1,INTEGER:2,BITSTRING:3,OCTETSTRING:4,NULL:5,OID:6,ODESC:7,EXTERNAL:8,REAL:9,ENUMERATED:10,EMBEDDED:11,UTF8:12,ROID:13,SEQUENCE:16,SET:17,PRINTABLESTRING:19,IA5STRING:22,UTCTIME:23,GENERALIZEDTIME:24,BMPSTRING:30};X.create=function(e,t,r,n,i){if(Ze.util.isArray(n)){for(var a=[],s=0;st){var n=new Error("Too few bytes to parse DER.");throw n.available=e.length(),n.remaining=t,n.requested=r,n}}var DC=function(e,t){var r=e.getByte();if(t--,r!==128){var n,i=r&128;if(!i)n=r;else{var a=r&127;Va(e,t,a),n=e.getInt(a<<3)}if(n<0)throw new Error("Negative length: "+n);return n}};X.fromDer=function(e,t){t===void 0&&(t={strict:!0,parseAllBytes:!0,decodeBitStrings:!0}),typeof t=="boolean"&&(t={strict:t,parseAllBytes:!0,decodeBitStrings:!0}),"strict"in t||(t.strict=!0),"parseAllBytes"in t||(t.parseAllBytes=!0),"decodeBitStrings"in t||(t.decodeBitStrings=!0),typeof e=="string"&&(e=Ze.util.createBuffer(e));var r=e.length(),n=Co(e,e.length(),0,t);if(t.parseAllBytes&&e.length()!==0){var i=new Error("Unparsed DER bytes remain after ASN.1 parsing.");throw i.byteCount=r,i.remaining=e.length(),i}return n};function Co(e,t,r,n){var i;Va(e,t,2);var a=e.getByte();t--;var s=a&192,o=a&31;i=e.length();var l=DC(e,t);if(t-=i-e.length(),l!==void 0&&l>t){if(n.strict){var u=new Error("Too few bytes to read ASN.1 value.");throw u.available=e.length(),u.remaining=t,u.requested=l,u}l=t}var c,f,d=(a&32)===32;if(d)if(c=[],l===void 0)for(;;){if(Va(e,t,2),e.bytes(2)===String.fromCharCode(0,0)){e.getBytes(2),t-=2;break}i=e.length(),c.push(Co(e,t,r+1,n)),t-=i-e.length()}else for(;l>0;)i=e.length(),c.push(Co(e,l,r+1,n)),t-=i-e.length(),l-=i-e.length();if(c===void 0&&s===X.Class.UNIVERSAL&&o===X.Type.BITSTRING&&(f=e.bytes(l)),c===void 0&&n.decodeBitStrings&&s===X.Class.UNIVERSAL&&o===X.Type.BITSTRING&&l>1){var v=e.read,g=t,C=0;if(o===X.Type.BITSTRING&&(Va(e,t,1),C=e.getByte(),t--),C===0)try{i=e.length();var I={strict:!0,decodeBitStrings:!0},y=Co(e,t,r+1,I),m=i-e.length();t-=m,o==X.Type.BITSTRING&&m++;var S=y.tagClass;m===l&&(S===X.Class.UNIVERSAL||S===X.Class.CONTEXT_SPECIFIC)&&(c=[y])}catch{}c===void 0&&(e.read=v,t=g)}if(c===void 0){if(l===void 0){if(n.strict)throw new Error("Non-constructed ASN.1 object of indefinite length.");l=t}if(o===X.Type.BMPSTRING)for(c="";l>0;l-=2)Va(e,t,2),c+=String.fromCharCode(e.getInt16()),t-=2;else c=e.getBytes(l),t-=l}var B=f===void 0?null:{bitStringContents:f};return X.create(s,o,d,c,B)}X.toDer=function(e){var t=Ze.util.createBuffer(),r=e.tagClass|e.type,n=Ze.util.createBuffer(),i=!1;if("bitStringContents"in e&&(i=!0,e.original&&(i=X.equals(e,e.original))),i)n.putBytes(e.bitStringContents);else if(e.composed){e.constructed?r|=32:n.putByte(0);for(var a=0;a1&&(e.value.charCodeAt(0)===0&&!(e.value.charCodeAt(1)&128)||e.value.charCodeAt(0)===255&&(e.value.charCodeAt(1)&128)===128)?n.putBytes(e.value.substr(1)):n.putBytes(e.value);if(t.putByte(r),n.length()<=127)t.putByte(n.length()&127);else{var s=n.length(),o="";do o+=String.fromCharCode(s&255),s=s>>>8;while(s>0);t.putByte(o.length|128);for(var a=o.length-1;a>=0;--a)t.putByte(o.charCodeAt(a))}return t.putBuffer(n),t};X.oidToDer=function(e){var t=e.split("."),r=Ze.util.createBuffer();r.putByte(40*parseInt(t[0],10)+parseInt(t[1],10));for(var n,i,a,s,o=2;o>>7,n||(s|=128),i.push(s),n=!1;while(a>0);for(var l=i.length-1;l>=0;--l)r.putByte(i[l])}return r};X.derToOid=function(e){var t;typeof e=="string"&&(e=Ze.util.createBuffer(e));var r=e.getByte();t=Math.floor(r/40)+"."+r%40;for(var n=0;e.length()>0;)r=e.getByte(),n=n<<7,r&128?n+=r&127:(t+="."+(n+r),n=0);return t};X.utcTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,2),10);r=r>=50?1900+r:2e3+r;var n=parseInt(e.substr(2,2),10)-1,i=parseInt(e.substr(4,2),10),a=parseInt(e.substr(6,2),10),s=parseInt(e.substr(8,2),10),o=0;if(e.length>11){var l=e.charAt(10),u=10;l!=="+"&&l!=="-"&&(o=parseInt(e.substr(10,2),10),u+=2)}if(t.setUTCFullYear(r,n,i),t.setUTCHours(a,s,o,0),u&&(l=e.charAt(u),l==="+"||l==="-")){var c=parseInt(e.substr(u+1,2),10),f=parseInt(e.substr(u+4,2),10),d=c*60+f;d*=6e4,l==="+"?t.setTime(+t-d):t.setTime(+t+d)}return t};X.generalizedTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,4),10),n=parseInt(e.substr(4,2),10)-1,i=parseInt(e.substr(6,2),10),a=parseInt(e.substr(8,2),10),s=parseInt(e.substr(10,2),10),o=parseInt(e.substr(12,2),10),l=0,u=0,c=!1;e.charAt(e.length-1)==="Z"&&(c=!0);var f=e.length-5,d=e.charAt(f);if(d==="+"||d==="-"){var v=parseInt(e.substr(f+1,2),10),g=parseInt(e.substr(f+4,2),10);u=v*60+g,u*=6e4,d==="+"&&(u*=-1),c=!0}return e.charAt(14)==="."&&(l=parseFloat(e.substr(14),10)*1e3),c?(t.setUTCFullYear(r,n,i),t.setUTCHours(a,s,o,l),t.setTime(+t+u)):(t.setFullYear(r,n,i),t.setHours(a,s,o,l)),t};X.dateToUtcTime=function(e){if(typeof e=="string")return e;var t="",r=[];r.push((""+e.getUTCFullYear()).substr(2)),r.push(""+(e.getUTCMonth()+1)),r.push(""+e.getUTCDate()),r.push(""+e.getUTCHours()),r.push(""+e.getUTCMinutes()),r.push(""+e.getUTCSeconds());for(var n=0;n=-128&&e<128)return t.putSignedInt(e,8);if(e>=-32768&&e<32768)return t.putSignedInt(e,16);if(e>=-8388608&&e<8388608)return t.putSignedInt(e,24);if(e>=-2147483648&&e<2147483648)return t.putSignedInt(e,32);var r=new Error("Integer too large; max is 32-bits.");throw r.integer=e,r};X.derToInteger=function(e){typeof e=="string"&&(e=Ze.util.createBuffer(e));var t=e.length()*8;if(t>32)throw new Error("Integer too large; max is 32-bits.");return e.getSignedInt(t)};X.validate=function(e,t,r,n){var i=!1;if((e.tagClass===t.tagClass||typeof t.tagClass>"u")&&(e.type===t.type||typeof t.type>"u"))if(e.constructed===t.constructed||typeof t.constructed>"u"){if(i=!0,t.value&&Ze.util.isArray(t.value))for(var a=0,s=0;i&&s0&&(n+=` -`);for(var i="",a=0;a1?n+="0x"+Ze.util.bytesToHex(e.value.slice(1)):n+="(none)",e.value.length>0){var u=e.value.charCodeAt(0);u==1?n+=" (1 unused bit shown)":u>1&&(n+=" ("+u+" unused bits shown)")}}else if(e.type===X.Type.OCTETSTRING)sh.test(e.value)||(n+="("+e.value+") "),n+="0x"+Ze.util.bytesToHex(e.value);else if(e.type===X.Type.UTF8)try{n+=Ze.util.decodeUtf8(e.value)}catch(c){if(c.message==="URI malformed")n+="0x"+Ze.util.bytesToHex(e.value)+" (malformed UTF8)";else throw c}else e.type===X.Type.PRINTABLESTRING||e.type===X.Type.IA5String?n+=e.value:sh.test(e.value)?n+="0x"+Ze.util.bytesToHex(e.value):e.value.length===0?n+="[null]":n+=e.value}return n};var Qo=me;Qo.md=Qo.md||{};Qo.md.algorithms=Qo.md.algorithms||{};var on=me,RC=on.hmac=on.hmac||{};RC.create=function(){var e=null,t=null,r=null,n=null,i={};return i.start=function(a,s){if(a!==null)if(typeof a=="string")if(a=a.toLowerCase(),a in on.md.algorithms)t=on.md.algorithms[a].create();else throw new Error('Unknown hash algorithm "'+a+'"');else t=a;if(s===null)s=e;else{if(typeof s=="string")s=on.util.createBuffer(s);else if(on.util.isArray(s)){var o=s;s=on.util.createBuffer();for(var l=0;lt.blockLength&&(t.start(),t.update(s.bytes()),s=t.digest()),r=on.util.createBuffer(),n=on.util.createBuffer(),u=s.length();for(var l=0;l>>0,s>>>0];for(var o=n.fullMessageLength.length-1;o>=0;--o)n.fullMessageLength[o]+=s[1],s[1]=s[0]+(n.fullMessageLength[o]/4294967296>>>0),n.fullMessageLength[o]=n.fullMessageLength[o]>>>0,s[0]=s[1]/4294967296>>>0;return t.putBytes(i),oh(e,r,t),(t.read>2048||t.length()===0)&&t.compact(),n},n.digest=function(){var i=Wr.util.createBuffer();i.putBytes(t.bytes());var a=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize,s=a&n.blockLength-1;i.putBytes(Lc.substr(0,n.blockLength-s));for(var o,l=0,u=n.fullMessageLength.length-1;u>=0;--u)o=n.fullMessageLength[u]*8+l,l=o/4294967296>>>0,i.putInt32Le(o>>>0);var c={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3};oh(c,r,i);var f=Wr.util.createBuffer();return f.putInt32Le(c.h0),f.putInt32Le(c.h1),f.putInt32Le(c.h2),f.putInt32Le(c.h3),f},n};var Lc=null,Eo=null,ja=null,Gi=null,m1=!1;function LC(){Lc=String.fromCharCode(128),Lc+=Wr.util.fillString(String.fromCharCode(0),64),Eo=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9],ja=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21],Gi=new Array(64);for(var e=0;e<64;++e)Gi[e]=Math.floor(Math.abs(Math.sin(e+1))*4294967296);m1=!0}function oh(e,t,r){for(var n,i,a,s,o,l,u,c,f=r.length();f>=64;){for(i=e.h0,a=e.h1,s=e.h2,o=e.h3,c=0;c<16;++c)t[c]=r.getInt32Le(),l=o^a&(s^o),n=i+l+Gi[c]+t[c],u=ja[c],i=o,o=s,s=a,a+=n<>>32-u;for(;c<32;++c)l=s^o&(a^s),n=i+l+Gi[c]+t[Eo[c]],u=ja[c],i=o,o=s,s=a,a+=n<>>32-u;for(;c<48;++c)l=a^s^o,n=i+l+Gi[c]+t[Eo[c]],u=ja[c],i=o,o=s,s=a,a+=n<>>32-u;for(;c<64;++c)l=s^(a|~o),n=i+l+Gi[c]+t[Eo[c]],u=ja[c],i=o,o=s,s=a,a+=n<>>32-u;e.h0=e.h0+i|0,e.h1=e.h1+a|0,e.h2=e.h2+s|0,e.h3=e.h3+o|0,f-=64}}var Wo=me,C1=Wo.pem=Wo.pem||{};C1.encode=function(e,t){t=t||{};var r="-----BEGIN "+e.type+`-----\r -`,n;if(e.procType&&(n={name:"Proc-Type",values:[String(e.procType.version),e.procType.type]},r+=eo(n)),e.contentDomain&&(n={name:"Content-Domain",values:[e.contentDomain]},r+=eo(n)),e.dekInfo&&(n={name:"DEK-Info",values:[e.dekInfo.algorithm]},e.dekInfo.parameters&&n.values.push(e.dekInfo.parameters),r+=eo(n)),e.headers)for(var i=0;i65&&s!==-1){var o=t[s];o===","?(++s,t=t.substr(0,s)+`\r - `+t.substr(s)):t=t.substr(0,s)+`\r -`+o+t.substr(s+1),a=i-s-1,s=-1,++i}else(t[i]===" "||t[i]===" "||t[i]===",")&&(s=i);return t}function OC(e){return e.replace(/^\s+/,"")}var nt=me;nt.des=nt.des||{};nt.des.startEncrypting=function(e,t,r,n){var i=_l({key:e,output:r,decrypt:!1,mode:n||(t===null?"ECB":"CBC")});return i.start(t),i};nt.des.createEncryptionCipher=function(e,t){return _l({key:e,output:null,decrypt:!1,mode:t})};nt.des.startDecrypting=function(e,t,r,n){var i=_l({key:e,output:r,decrypt:!0,mode:n||(t===null?"ECB":"CBC")});return i.start(t),i};nt.des.createDecryptionCipher=function(e,t){return _l({key:e,output:null,decrypt:!0,mode:t})};nt.des.Algorithm=function(e,t){var r=this;r.name=e,r.mode=new t({blockSize:8,cipher:{encrypt:function(n,i){return lh(r._keys,n,i,!1)},decrypt:function(n,i){return lh(r._keys,n,i,!0)}}}),r._init=!1};nt.des.Algorithm.prototype.initialize=function(e){if(!this._init){var t=nt.util.createBuffer(e.key);if(this.name.indexOf("3DES")===0&&t.length()!==24)throw new Error("Invalid Triple-DES key size: "+t.length()*8);this._keys=HC(t),this._init=!0}};rn("DES-ECB",nt.cipher.modes.ecb);rn("DES-CBC",nt.cipher.modes.cbc);rn("DES-CFB",nt.cipher.modes.cfb);rn("DES-OFB",nt.cipher.modes.ofb);rn("DES-CTR",nt.cipher.modes.ctr);rn("3DES-ECB",nt.cipher.modes.ecb);rn("3DES-CBC",nt.cipher.modes.cbc);rn("3DES-CFB",nt.cipher.modes.cfb);rn("3DES-OFB",nt.cipher.modes.ofb);rn("3DES-CTR",nt.cipher.modes.ctr);function rn(e,t){var r=function(){return new nt.des.Algorithm(e,t)};nt.cipher.registerAlgorithm(e,r)}var PC=[16843776,0,65536,16843780,16842756,66564,4,65536,1024,16843776,16843780,1024,16778244,16842756,16777216,4,1028,16778240,16778240,66560,66560,16842752,16842752,16778244,65540,16777220,16777220,65540,0,1028,66564,16777216,65536,16843780,4,16842752,16843776,16777216,16777216,1024,16842756,65536,66560,16777220,1024,4,16778244,66564,16843780,65540,16842752,16778244,16777220,1028,66564,16843776,1028,16778240,16778240,0,65540,66560,0,16842756],UC=[-2146402272,-2147450880,32768,1081376,1048576,32,-2146435040,-2147450848,-2147483616,-2146402272,-2146402304,-2147483648,-2147450880,1048576,32,-2146435040,1081344,1048608,-2147450848,0,-2147483648,32768,1081376,-2146435072,1048608,-2147483616,0,1081344,32800,-2146402304,-2146435072,32800,0,1081376,-2146435040,1048576,-2147450848,-2146435072,-2146402304,32768,-2146435072,-2147450880,32,-2146402272,1081376,32,32768,-2147483648,32800,-2146402304,1048576,-2147483616,1048608,-2147450848,-2147483616,1048608,1081344,0,-2147450880,32800,-2147483648,-2146435040,-2146402272,1081344],MC=[520,134349312,0,134348808,134218240,0,131592,134218240,131080,134217736,134217736,131072,134349320,131080,134348800,520,134217728,8,134349312,512,131584,134348800,134348808,131592,134218248,131584,131072,134218248,8,134349320,512,134217728,134349312,134217728,131080,520,131072,134349312,134218240,0,512,131080,134349320,134218240,134217736,512,0,134348808,134218248,131072,134217728,134349320,8,131592,131584,134217736,134348800,134218248,520,134348800,131592,8,134348808,131584],FC=[8396801,8321,8321,128,8396928,8388737,8388609,8193,0,8396800,8396800,8396929,129,0,8388736,8388609,1,8192,8388608,8396801,128,8388608,8193,8320,8388737,1,8320,8388736,8192,8396928,8396929,129,8388736,8388609,8396800,8396929,129,0,0,8396800,8320,8388736,8388737,1,8396801,8321,8321,128,8396929,129,1,8192,8388609,8193,8396928,8388737,8193,8320,8388608,8396801,128,8388608,8192,8396928],VC=[256,34078976,34078720,1107296512,524288,256,1073741824,34078720,1074266368,524288,33554688,1074266368,1107296512,1107820544,524544,1073741824,33554432,1074266112,1074266112,0,1073742080,1107820800,1107820800,33554688,1107820544,1073742080,0,1107296256,34078976,33554432,1107296256,524544,524288,1107296512,256,33554432,1073741824,34078720,1107296512,1074266368,33554688,1073741824,1107820544,34078976,1074266368,256,33554432,1107820544,1107820800,524544,1107296256,1107820800,34078720,0,1074266112,1107296256,524544,33554688,1073742080,524288,0,1074266112,34078976,1073742080],jC=[536870928,541065216,16384,541081616,541065216,16,541081616,4194304,536887296,4210704,4194304,536870928,4194320,536887296,536870912,16400,0,4194320,536887312,16384,4210688,536887312,16,541065232,541065232,0,4210704,541081600,16400,4210688,541081600,536870912,536887296,16,541065232,4210688,541081616,4194304,16400,536870928,4194304,536887296,536870912,16400,536870928,541081616,4210688,541065216,4210704,541081600,0,541065232,16,16384,541065216,4210704,16384,4194320,536887312,0,541081600,536870912,4194320,536887312],KC=[2097152,69206018,67110914,0,2048,67110914,2099202,69208064,69208066,2097152,0,67108866,2,67108864,69206018,2050,67110912,2099202,2097154,67110912,67108866,69206016,69208064,2097154,69206016,2048,2050,69208066,2099200,2,67108864,2099200,67108864,2099200,2097152,67110914,67110914,69206018,69206018,2,2097154,67108864,67110912,2097152,69208064,2050,2099202,69208064,2050,67108866,69208066,69206016,2099200,0,2,69208066,0,2099202,69206016,2048,67108866,67110912,2048,2097154],zC=[268439616,4096,262144,268701760,268435456,268439616,64,268435456,262208,268697600,268701760,266240,268701696,266304,4096,64,268697600,268435520,268439552,4160,266240,262208,268697664,268701696,4160,0,0,268697664,268435520,268439552,266304,262144,266304,262144,268701696,4096,64,268697664,4096,266304,268439552,64,268435520,268697600,268697664,268435456,262144,268439616,0,268701760,262208,268435520,268697600,268439552,268439616,0,268701760,266240,266240,4160,4160,262208,268435456,268701696];function HC(e){for(var t=[0,4,536870912,536870916,65536,65540,536936448,536936452,512,516,536871424,536871428,66048,66052,536936960,536936964],r=[0,1,1048576,1048577,67108864,67108865,68157440,68157441,256,257,1048832,1048833,67109120,67109121,68157696,68157697],n=[0,8,2048,2056,16777216,16777224,16779264,16779272,0,8,2048,2056,16777216,16777224,16779264,16779272],i=[0,2097152,134217728,136314880,8192,2105344,134225920,136323072,131072,2228224,134348800,136445952,139264,2236416,134356992,136454144],a=[0,262144,16,262160,0,262144,16,262160,4096,266240,4112,266256,4096,266240,4112,266256],s=[0,1024,32,1056,0,1024,32,1056,33554432,33555456,33554464,33555488,33554432,33555456,33554464,33555488],o=[0,268435456,524288,268959744,2,268435458,524290,268959746,0,268435456,524288,268959744,2,268435458,524290,268959746],l=[0,65536,2048,67584,536870912,536936448,536872960,536938496,131072,196608,133120,198656,537001984,537067520,537004032,537069568],u=[0,262144,0,262144,2,262146,2,262146,33554432,33816576,33554432,33816576,33554434,33816578,33554434,33816578],c=[0,268435456,8,268435464,0,268435456,8,268435464,1024,268436480,1032,268436488,1024,268436480,1032,268436488],f=[0,32,0,32,1048576,1048608,1048576,1048608,8192,8224,8192,8224,1056768,1056800,1056768,1056800],d=[0,16777216,512,16777728,2097152,18874368,2097664,18874880,67108864,83886080,67109376,83886592,69206016,85983232,69206528,85983744],v=[0,4096,134217728,134221824,524288,528384,134742016,134746112,16,4112,134217744,134221840,524304,528400,134742032,134746128],g=[0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261],C=e.length()>8?3:1,I=[],y=[0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0],m=0,S,B=0;B>>4^L)&252645135,L^=S,R^=S<<4,S=(L>>>-16^R)&65535,R^=S,L^=S<<-16,S=(R>>>2^L)&858993459,L^=S,R^=S<<2,S=(L>>>-16^R)&65535,R^=S,L^=S<<-16,S=(R>>>1^L)&1431655765,L^=S,R^=S<<1,S=(L>>>8^R)&16711935,R^=S,L^=S<<8,S=(R>>>1^L)&1431655765,L^=S,R^=S<<1,S=R<<8|L>>>20&240,R=L<<24|L<<8&16711680|L>>>8&65280|L>>>24&240,L=S;for(var M=0;M>>26,L=L<<2|L>>>26):(R=R<<1|R>>>27,L=L<<1|L>>>27),R&=-15,L&=-15;var V=t[R>>>28]|r[R>>>24&15]|n[R>>>20&15]|i[R>>>16&15]|a[R>>>12&15]|s[R>>>8&15]|o[R>>>4&15],Q=l[L>>>28]|u[L>>>24&15]|c[L>>>20&15]|f[L>>>16&15]|d[L>>>12&15]|v[L>>>8&15]|g[L>>>4&15];S=(Q>>>16^V)&65535,I[m++]=V^S,I[m++]=Q^S<<16}}return I}function lh(e,t,r,n){var i=e.length===32?3:9,a;i===3?a=n?[30,-2,-2]:[0,32,2]:a=n?[94,62,-2,32,64,2,30,-2,-2]:[0,32,2,62,30,-2,64,96,2];var s,o=t[0],l=t[1];s=(o>>>4^l)&252645135,l^=s,o^=s<<4,s=(o>>>16^l)&65535,l^=s,o^=s<<16,s=(l>>>2^o)&858993459,o^=s,l^=s<<2,s=(l>>>8^o)&16711935,o^=s,l^=s<<8,s=(o>>>1^l)&1431655765,l^=s,o^=s<<1,o=o<<1|o>>>31,l=l<<1|l>>>31;for(var u=0;u>>4|l<<28)^e[d+1];s=o,o=l,l=s^(UC[v>>>24&63]|FC[v>>>16&63]|jC[v>>>8&63]|zC[v&63]|PC[g>>>24&63]|MC[g>>>16&63]|VC[g>>>8&63]|KC[g&63])}s=o,o=l,l=s}o=o>>>1|o<<31,l=l>>>1|l<<31,s=(o>>>1^l)&1431655765,l^=s,o^=s<<1,s=(l>>>8^o)&16711935,o^=s,l^=s<<8,s=(l>>>2^o)&858993459,o^=s,l^=s<<2,s=(o>>>16^l)&65535,l^=s,o^=s<<16,s=(o>>>4^l)&252645135,l^=s,o^=s<<4,r[0]=o,r[1]=l}function _l(e){e=e||{};var t=(e.mode||"CBC").toUpperCase(),r="DES-"+t,n;e.decrypt?n=nt.cipher.createDecipher(r,e.key):n=nt.cipher.createCipher(r,e.key);var i=n.start;return n.start=function(a,s){var o=null;s instanceof nt.util.ByteBuffer&&(o=s,s={}),s=s||{},s.output=o,s.iv=a,i.call(n,s)},n}const qC={},$C=Object.freeze(Object.defineProperty({__proto__:null,default:qC},Symbol.toStringTag,{value:"Module"})),i0=pv($C);var Vt=me,GC=Vt.pkcs5=Vt.pkcs5||{},an;Vt.util.isNodejs&&!Vt.options.usePureJavaScript&&(an=i0);Vt.pbkdf2=GC.pbkdf2=function(e,t,r,n,i,a){if(typeof i=="function"&&(a=i,i=null),Vt.util.isNodejs&&!Vt.options.usePureJavaScript&&an.pbkdf2&&(i===null||typeof i!="object")&&(an.pbkdf2Sync.length>4||!i||i==="sha1"))return typeof i!="string"&&(i="sha1"),e=Buffer.from(e,"binary"),t=Buffer.from(t,"binary"),a?an.pbkdf2Sync.length===4?an.pbkdf2(e,t,r,n,function(S,B){if(S)return a(S);a(null,B.toString("binary"))}):an.pbkdf2(e,t,r,n,i,function(S,B){if(S)return a(S);a(null,B.toString("binary"))}):an.pbkdf2Sync.length===4?an.pbkdf2Sync(e,t,r,n).toString("binary"):an.pbkdf2Sync(e,t,r,n,i).toString("binary");if((typeof i>"u"||i===null)&&(i="sha1"),typeof i=="string"){if(!(i in Vt.md.algorithms))throw new Error("Unknown hash algorithm: "+i);i=Vt.md[i].create()}var s=i.digestLength;if(n>4294967295*s){var o=new Error("Derived key is too long.");if(a)return a(o);throw o}var l=Math.ceil(n/s),u=n-(l-1)*s,c=Vt.hmac.create();c.start(i,e);var f="",d,v,g;if(!a){for(var C=1;C<=l;++C){c.start(null,null),c.update(t),c.update(Vt.util.int32ToBytes(C)),d=g=c.digest().getBytes();for(var I=2;I<=r;++I)c.start(null,null),c.update(g),v=c.digest().getBytes(),d=Vt.util.xorBytes(d,v,s),g=v;f+=Cl)return a(null,f);c.start(null,null),c.update(t),c.update(Vt.util.int32ToBytes(C)),d=g=c.digest().getBytes(),I=2,m()}function m(){if(I<=r)return c.start(null,null),c.update(g),v=c.digest().getBytes(),d=Vt.util.xorBytes(d,v,s),g=v,++I,Vt.util.setImmediate(m);f+=C>>0,s>>>0];for(var o=n.fullMessageLength.length-1;o>=0;--o)n.fullMessageLength[o]+=s[1],s[1]=s[0]+(n.fullMessageLength[o]/4294967296>>>0),n.fullMessageLength[o]=n.fullMessageLength[o]>>>0,s[0]=s[1]/4294967296>>>0;return t.putBytes(i),uh(e,r,t),(t.read>2048||t.length()===0)&&t.compact(),n},n.digest=function(){var i=Yr.util.createBuffer();i.putBytes(t.bytes());var a=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize,s=a&n.blockLength-1;i.putBytes(Oc.substr(0,n.blockLength-s));for(var o,l,u=n.fullMessageLength[0]*8,c=0;c>>0,u+=l,i.putInt32(u>>>0),u=o>>>0;i.putInt32(u);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4,h5:e.h5,h6:e.h6,h7:e.h7};uh(f,r,i);var d=Yr.util.createBuffer();return d.putInt32(f.h0),d.putInt32(f.h1),d.putInt32(f.h2),d.putInt32(f.h3),d.putInt32(f.h4),d.putInt32(f.h5),d.putInt32(f.h6),d.putInt32(f.h7),d},n};var Oc=null,S1=!1,x1=null;function QC(){Oc=String.fromCharCode(128),Oc+=Yr.util.fillString(String.fromCharCode(0),64),x1=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],S1=!0}function uh(e,t,r){for(var n,i,a,s,o,l,u,c,f,d,v,g,C,I,y,m=r.length();m>=64;){for(u=0;u<16;++u)t[u]=r.getInt32();for(;u<64;++u)n=t[u-2],n=(n>>>17|n<<15)^(n>>>19|n<<13)^n>>>10,i=t[u-15],i=(i>>>7|i<<25)^(i>>>18|i<<14)^i>>>3,t[u]=n+t[u-7]+i+t[u-16]|0;for(c=e.h0,f=e.h1,d=e.h2,v=e.h3,g=e.h4,C=e.h5,I=e.h6,y=e.h7,u=0;u<64;++u)s=(g>>>6|g<<26)^(g>>>11|g<<21)^(g>>>25|g<<7),o=I^g&(C^I),a=(c>>>2|c<<30)^(c>>>13|c<<19)^(c>>>22|c<<10),l=c&f|d&(c^f),n=y+s+o+x1[u]+t[u],i=a+l,y=I,I=C,C=g,g=v+n>>>0,v=d,d=f,f=c,c=n+i>>>0;e.h0=e.h0+c|0,e.h1=e.h1+f|0,e.h2=e.h2+d|0,e.h3=e.h3+v|0,e.h4=e.h4+g|0,e.h5=e.h5+C|0,e.h6=e.h6+I|0,e.h7=e.h7+y|0,m-=64}}var $r=me,So=null;$r.util.isNodejs&&!$r.options.usePureJavaScript&&!process.versions["node-webkit"]&&(So=i0);var WC=$r.prng=$r.prng||{};WC.create=function(e){for(var t={plugin:e,key:null,seed:null,time:null,reseeds:0,generated:0,keyBytes:""},r=e.md,n=new Array(32),i=0;i<32;++i)n[i]=r.create();t.pools=n,t.pool=0,t.generate=function(u,c){if(!c)return t.generateSync(u);var f=t.plugin.cipher,d=t.plugin.increment,v=t.plugin.formatKey,g=t.plugin.formatSeed,C=$r.util.createBuffer();t.key=null,I();function I(y){if(y)return c(y);if(C.length()>=u)return c(null,C.getBytes(u));if(t.generated>1048575&&(t.key=null),t.key===null)return $r.util.nextTick(function(){a(I)});var m=f(t.key,t.seed);t.generated+=m.length,C.putBytes(m),t.key=v(f(t.key,d(t.seed))),t.seed=g(f(t.key,t.seed)),$r.util.setImmediate(I)}},t.generateSync=function(u){var c=t.plugin.cipher,f=t.plugin.increment,d=t.plugin.formatKey,v=t.plugin.formatSeed;t.key=null;for(var g=$r.util.createBuffer();g.length()1048575&&(t.key=null),t.key===null&&s();var C=c(t.key,t.seed);t.generated+=C.length,g.putBytes(C),t.key=d(c(t.key,f(t.seed))),t.seed=v(c(t.key,t.seed))}return g.getBytes(u)};function a(u){if(t.pools[0].messageLength>=32)return o(),u();var c=32-t.pools[0].messageLength<<5;t.seedFile(c,function(f,d){if(f)return u(f);t.collect(d),o(),u()})}function s(){if(t.pools[0].messageLength>=32)return o();var u=32-t.pools[0].messageLength<<5;t.collect(t.seedFileSync(u)),o()}function o(){t.reseeds=t.reseeds===4294967295?0:t.reseeds+1;var u=t.plugin.md.create();u.update(t.keyBytes);for(var c=1,f=0;f<32;++f)t.reseeds%c===0&&(u.update(t.pools[f].digest().getBytes()),t.pools[f].start()),c=c<<1;t.keyBytes=u.digest().getBytes(),u.start(),u.update(t.keyBytes);var d=u.digest().getBytes();t.key=t.plugin.formatKey(t.keyBytes),t.seed=t.plugin.formatSeed(d),t.generated=0}function l(u){var c=null,f=$r.util.globalScope,d=f.crypto||f.msCrypto;d&&d.getRandomValues&&(c=function(R){return d.getRandomValues(R)});var v=$r.util.createBuffer();if(c)for(;v.length()>16),m+=(y&32767)<<16,m+=y>>15,m=(m&2147483647)+(m>>31),B=m&4294967295;for(var I=0;I<3;++I)S=B>>>(I<<3),S^=Math.floor(Math.random()*256),v.putByte(S&255)}return v.getBytes(u)}return So?(t.seedFile=function(u,c){So.randomBytes(u,function(f,d){if(f)return c(f);c(null,d.toString())})},t.seedFileSync=function(u){return So.randomBytes(u).toString()}):(t.seedFile=function(u,c){try{c(null,l(u))}catch(f){c(f)}},t.seedFileSync=l),t.collect=function(u){for(var c=u.length,f=0;f>d&255);t.collect(f)},t.registerWorker=function(u){if(u===self)t.seedFile=function(f,d){function v(g){var C=g.data;C.forge&&C.forge.prng&&(self.removeEventListener("message",v),d(C.forge.prng.err,C.forge.prng.bytes))}self.addEventListener("message",v),self.postMessage({forge:{prng:{needed:f}}})};else{var c=function(f){var d=f.data;d.forge&&d.forge.prng&&t.seedFile(d.forge.prng.needed,function(v,g){u.postMessage({forge:{prng:{err:v,bytes:g}}})})};u.addEventListener("message",c)}},t};var Ct=me;(function(){if(Ct.random&&Ct.random.getBytes){Ct.random;return}(function(e){var t={},r=new Array(4),n=Ct.util.createBuffer();t.formatKey=function(f){var d=Ct.util.createBuffer(f);return f=new Array(4),f[0]=d.getInt32(),f[1]=d.getInt32(),f[2]=d.getInt32(),f[3]=d.getInt32(),Ct.aes._expandKey(f,!1)},t.formatSeed=function(f){var d=Ct.util.createBuffer(f);return f=new Array(4),f[0]=d.getInt32(),f[1]=d.getInt32(),f[2]=d.getInt32(),f[3]=d.getInt32(),f},t.cipher=function(f,d){return Ct.aes._updateBlock(f,d,r,!1),n.putInt32(r[0]),n.putInt32(r[1]),n.putInt32(r[2]),n.putInt32(r[3]),n.getBytes()},t.increment=function(f){return++f[3],f},t.md=Ct.md.sha256;function i(){var f=Ct.prng.create(t);return f.getBytes=function(d,v){return f.generate(d,v)},f.getBytesSync=function(d){return f.generate(d)},f}var a=i(),s=null,o=Ct.util.globalScope,l=o.crypto||o.msCrypto;if(l&&l.getRandomValues&&(s=function(f){return l.getRandomValues(f)}),Ct.options.usePureJavaScript||!Ct.util.isNodejs&&!s){if(a.collectInt(+new Date,32),typeof navigator<"u"){var u="";for(var c in navigator)try{typeof navigator[c]=="string"&&(u+=navigator[c])}catch{}a.collect(u),u=null}e&&(e().mousemove(function(f){a.collectInt(f.clientX,16),a.collectInt(f.clientY,16)}),e().keypress(function(f){a.collectInt(f.charCode,8)}))}if(!Ct.random)Ct.random=a;else for(var c in a)Ct.random[c]=a[c];Ct.random.createInstance=i,Ct.random})(typeof jQuery<"u"?jQuery:null)})();var Yt=me,Su=[217,120,249,196,25,221,181,237,40,233,253,121,74,160,216,157,198,126,55,131,43,118,83,142,98,76,100,136,68,139,251,162,23,154,89,245,135,179,79,19,97,69,109,141,9,129,125,50,189,143,64,235,134,183,123,11,240,149,33,34,92,107,78,130,84,214,101,147,206,96,178,28,115,86,192,20,167,140,241,220,18,117,202,31,59,190,228,209,66,61,212,48,163,60,182,38,111,191,14,218,70,105,7,87,39,242,29,155,188,148,67,3,248,17,199,246,144,239,62,231,6,195,213,47,200,102,30,215,8,232,234,222,128,82,238,247,132,170,114,172,53,77,106,42,150,26,210,113,90,21,73,116,75,159,208,94,4,24,164,236,194,224,65,110,15,81,203,204,36,145,175,80,161,244,112,57,153,124,58,133,35,184,180,122,252,2,54,91,37,85,151,49,45,93,250,152,227,138,146,174,5,223,41,16,103,108,186,201,211,0,230,207,225,158,168,44,99,22,1,63,88,226,137,169,13,56,52,27,171,51,255,176,187,72,12,95,185,177,205,46,197,243,219,71,229,165,156,119,10,166,32,104,254,127,193,173],ch=[1,2,3,5],YC=function(e,t){return e<>16-t},XC=function(e,t){return(e&65535)>>t|e<<16-t&65535};Yt.rc2=Yt.rc2||{};Yt.rc2.expandKey=function(e,t){typeof e=="string"&&(e=Yt.util.createBuffer(e)),t=t||128;var r=e,n=e.length(),i=t,a=Math.ceil(i/8),s=255>>(i&7),o;for(o=n;o<128;o++)r.putByte(Su[r.at(o-1)+r.at(o-n)&255]);for(r.setAt(128-a,Su[r.at(128-a)&s]),o=127-a;o>=0;o--)r.setAt(o,Su[r.at(o+1)^r.at(o+a)]);return r};var T1=function(e,t,r){var n=!1,i=null,a=null,s=null,o,l,u,c,f=[];for(e=Yt.rc2.expandKey(e,t),u=0;u<64;u++)f.push(e.getInt16Le());r?(o=function(g){for(u=0;u<4;u++)g[u]+=f[c]+(g[(u+3)%4]&g[(u+2)%4])+(~g[(u+3)%4]&g[(u+1)%4]),g[u]=YC(g[u],ch[u]),c++},l=function(g){for(u=0;u<4;u++)g[u]+=f[g[(u+3)%4]&63]}):(o=function(g){for(u=3;u>=0;u--)g[u]=XC(g[u],ch[u]),g[u]-=f[c]+(g[(u+3)%4]&g[(u+2)%4])+(~g[(u+3)%4]&g[(u+1)%4]),c--},l=function(g){for(u=3;u>=0;u--)g[u]-=f[g[(u+3)%4]&63]});var d=function(g){var C=[];for(u=0;u<4;u++){var I=i.getInt16Le();s!==null&&(r?I^=s.getInt16Le():s.putInt16Le(I)),C.push(I&65535)}c=r?0:63;for(var y=0;y=8;)d([[5,o],[1,l],[6,o],[1,l],[5,o]])},finish:function(g){var C=!0;if(r)if(g)C=g(8,i,!r);else{var I=i.length()===8?8:8-i.length();i.fillWithByte(I,I)}if(C&&(n=!0,v.update()),!r&&(C=i.length()===0,C))if(g)C=g(8,a,!r);else{var y=a.length(),m=a.at(y-1);m>y?C=!1:a.truncate(m)}return C}},v};Yt.rc2.startEncrypting=function(e,t,r){var n=Yt.rc2.createEncryptionCipher(e,128);return n.start(t,r),n};Yt.rc2.createEncryptionCipher=function(e,t){return T1(e,t,!0)};Yt.rc2.startDecrypting=function(e,t,r){var n=Yt.rc2.createDecryptionCipher(e,128);return n.start(t,r),n};Yt.rc2.createDecryptionCipher=function(e,t){return T1(e,t,!1)};var Pc=me;Pc.jsbn=Pc.jsbn||{};var fn;function K(e,t,r){this.data=[],e!=null&&(typeof e=="number"?this.fromNumber(e,t,r):t==null&&typeof e!="string"?this.fromString(e,256):this.fromString(e,t))}Pc.jsbn.BigInteger=K;function Be(){return new K(null)}function ZC(e,t,r,n,i,a){for(;--a>=0;){var s=t*this.data[e++]+r.data[n]+i;i=Math.floor(s/67108864),r.data[n++]=s&67108863}return i}function JC(e,t,r,n,i,a){for(var s=t&32767,o=t>>15;--a>=0;){var l=this.data[e]&32767,u=this.data[e++]>>15,c=o*l+u*s;l=s*l+((c&32767)<<15)+r.data[n]+(i&1073741823),i=(l>>>30)+(c>>>15)+o*u+(i>>>30),r.data[n++]=l&1073741823}return i}function fh(e,t,r,n,i,a){for(var s=t&16383,o=t>>14;--a>=0;){var l=this.data[e]&16383,u=this.data[e++]>>14,c=o*l+u*s;l=s*l+((c&16383)<<14)+r.data[n]+i,i=(l>>28)+(c>>14)+o*u,r.data[n++]=l&268435455}return i}typeof navigator>"u"?(K.prototype.am=fh,fn=28):navigator.appName=="Microsoft Internet Explorer"?(K.prototype.am=JC,fn=30):navigator.appName!="Netscape"?(K.prototype.am=ZC,fn=26):(K.prototype.am=fh,fn=28);K.prototype.DB=fn;K.prototype.DM=(1<=0;--t)e.data[t]=this.data[t];e.t=this.t,e.s=this.s}function rE(e){this.t=1,this.s=e<0?-1:0,e>0?this.data[0]=e:e<-1?this.data[0]=e+this.DV:this.t=0}function Pn(e){var t=Be();return t.fromInt(e),t}function nE(e,t){var r;if(t==16)r=4;else if(t==8)r=3;else if(t==256)r=8;else if(t==2)r=1;else if(t==32)r=5;else if(t==4)r=2;else{this.fromRadix(e,t);return}this.t=0,this.s=0;for(var n=e.length,i=!1,a=0;--n>=0;){var s=r==8?e[n]&255:w1(e,n);if(s<0){e.charAt(n)=="-"&&(i=!0);continue}i=!1,a==0?this.data[this.t++]=s:a+r>this.DB?(this.data[this.t-1]|=(s&(1<>this.DB-a):this.data[this.t-1]|=s<=this.DB&&(a-=this.DB)}r==8&&e[0]&128&&(this.s=-1,a>0&&(this.data[this.t-1]|=(1<0&&this.data[this.t-1]==e;)--this.t}function aE(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(e==16)t=4;else if(e==8)t=3;else if(e==2)t=1;else if(e==32)t=5;else if(e==4)t=2;else return this.toRadix(e);var r=(1<0)for(o>o)>0&&(i=!0,a=dh(n));s>=0;)o>(o+=this.DB-t)):(n=this.data[s]>>(o-=t)&r,o<=0&&(o+=this.DB,--s)),n>0&&(i=!0),i&&(a+=dh(n));return i?a:"0"}function sE(){var e=Be();return K.ZERO.subTo(this,e),e}function oE(){return this.s<0?this.negate():this}function lE(e){var t=this.s-e.s;if(t!=0)return t;var r=this.t;if(t=r-e.t,t!=0)return this.s<0?-t:t;for(;--r>=0;)if((t=this.data[r]-e.data[r])!=0)return t;return 0}function Bl(e){var t=1,r;return(r=e>>>16)!=0&&(e=r,t+=16),(r=e>>8)!=0&&(e=r,t+=8),(r=e>>4)!=0&&(e=r,t+=4),(r=e>>2)!=0&&(e=r,t+=2),(r=e>>1)!=0&&(e=r,t+=1),t}function uE(){return this.t<=0?0:this.DB*(this.t-1)+Bl(this.data[this.t-1]^this.s&this.DM)}function cE(e,t){var r;for(r=this.t-1;r>=0;--r)t.data[r+e]=this.data[r];for(r=e-1;r>=0;--r)t.data[r]=0;t.t=this.t+e,t.s=this.s}function fE(e,t){for(var r=e;r=0;--o)t.data[o+a+1]=this.data[o]>>n|s,s=(this.data[o]&i)<=0;--o)t.data[o]=0;t.data[a]=s,t.t=this.t+a+1,t.s=this.s,t.clamp()}function hE(e,t){t.s=this.s;var r=Math.floor(e/this.DB);if(r>=this.t){t.t=0;return}var n=e%this.DB,i=this.DB-n,a=(1<>n;for(var s=r+1;s>n;n>0&&(t.data[this.t-r-1]|=(this.s&a)<>=this.DB;if(e.t>=this.DB;n+=this.s}else{for(n+=this.s;r>=this.DB;n-=e.s}t.s=n<0?-1:0,n<-1?t.data[r++]=this.DV+n:n>0&&(t.data[r++]=n),t.t=r,t.clamp()}function gE(e,t){var r=this.abs(),n=e.abs(),i=r.t;for(t.t=i+n.t;--i>=0;)t.data[i]=0;for(i=0;i=0;)e.data[r]=0;for(r=0;r=t.DV&&(e.data[r+t.t]-=t.DV,e.data[r+t.t+1]=1)}e.t>0&&(e.data[e.t-1]+=t.am(r,t.data[r],e,2*r,0,1)),e.s=0,e.clamp()}function yE(e,t,r){var n=e.abs();if(!(n.t<=0)){var i=this.abs();if(i.t0?(n.lShiftTo(l,a),i.lShiftTo(l,r)):(n.copyTo(a),i.copyTo(r));var u=a.t,c=a.data[u-1];if(c!=0){var f=c*(1<1?a.data[u-2]>>this.F2:0),d=this.FV/f,v=(1<=0&&(r.data[r.t++]=1,r.subTo(y,r)),K.ONE.dlShiftTo(u,y),y.subTo(a,a);a.t=0;){var m=r.data[--C]==c?this.DM:Math.floor(r.data[C]*d+(r.data[C-1]+g)*v);if((r.data[C]+=a.am(0,m,r,I,0,u))0&&r.rShiftTo(l,r),s<0&&K.ZERO.subTo(r,r)}}}function mE(e){var t=Be();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(K.ZERO)>0&&e.subTo(t,t),t}function Ii(e){this.m=e}function CE(e){return e.s<0||e.compareTo(this.m)>=0?e.mod(this.m):e}function EE(e){return e}function SE(e){e.divRemTo(this.m,null,e)}function xE(e,t,r){e.multiplyTo(t,r),this.reduce(r)}function TE(e,t){e.squareTo(t),this.reduce(t)}Ii.prototype.convert=CE;Ii.prototype.revert=EE;Ii.prototype.reduce=SE;Ii.prototype.mulTo=xE;Ii.prototype.sqrTo=TE;function wE(){if(this.t<1)return 0;var e=this.data[0];if(!(e&1))return 0;var t=e&3;return t=t*(2-(e&15)*t)&15,t=t*(2-(e&255)*t)&255,t=t*(2-((e&65535)*t&65535))&65535,t=t*(2-e*t%this.DV)%this.DV,t>0?this.DV-t:-t}function _i(e){this.m=e,this.mp=e.invDigit(),this.mpl=this.mp&32767,this.mph=this.mp>>15,this.um=(1<0&&this.m.subTo(t,t),t}function _E(e){var t=Be();return e.copyTo(t),this.reduce(t),t}function AE(e){for(;e.t<=this.mt2;)e.data[e.t++]=0;for(var t=0;t>15)*this.mpl&this.um)<<15)&e.DM;for(r=t+this.m.t,e.data[r]+=this.m.am(0,n,e,t,0,this.m.t);e.data[r]>=e.DV;)e.data[r]-=e.DV,e.data[++r]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)}function BE(e,t){e.squareTo(t),this.reduce(t)}function kE(e,t,r){e.multiplyTo(t,r),this.reduce(r)}_i.prototype.convert=IE;_i.prototype.revert=_E;_i.prototype.reduce=AE;_i.prototype.mulTo=kE;_i.prototype.sqrTo=BE;function bE(){return(this.t>0?this.data[0]&1:this.s)==0}function NE(e,t){if(e>4294967295||e<1)return K.ONE;var r=Be(),n=Be(),i=t.convert(this),a=Bl(e)-1;for(i.copyTo(r);--a>=0;)if(t.sqrTo(r,n),(e&1<0)t.mulTo(n,i,r);else{var s=r;r=n,n=s}return t.revert(r)}function DE(e,t){var r;return e<256||t.isEven()?r=new Ii(t):r=new _i(t),this.exp(e,r)}K.prototype.copyTo=tE;K.prototype.fromInt=rE;K.prototype.fromString=nE;K.prototype.clamp=iE;K.prototype.dlShiftTo=cE;K.prototype.drShiftTo=fE;K.prototype.lShiftTo=dE;K.prototype.rShiftTo=hE;K.prototype.subTo=pE;K.prototype.multiplyTo=gE;K.prototype.squareTo=vE;K.prototype.divRemTo=yE;K.prototype.invDigit=wE;K.prototype.isEven=bE;K.prototype.exp=NE;K.prototype.toString=aE;K.prototype.negate=sE;K.prototype.abs=oE;K.prototype.compareTo=lE;K.prototype.bitLength=uE;K.prototype.mod=mE;K.prototype.modPowInt=DE;K.ZERO=Pn(0);K.ONE=Pn(1);function RE(){var e=Be();return this.copyTo(e),e}function LE(){if(this.s<0){if(this.t==1)return this.data[0]-this.DV;if(this.t==0)return-1}else{if(this.t==1)return this.data[0];if(this.t==0)return 0}return(this.data[1]&(1<<32-this.DB)-1)<>24}function PE(){return this.t==0?this.s:this.data[0]<<16>>16}function UE(e){return Math.floor(Math.LN2*this.DB/Math.log(e))}function ME(){return this.s<0?-1:this.t<=0||this.t==1&&this.data[0]<=0?0:1}function FE(e){if(e==null&&(e=10),this.signum()==0||e<2||e>36)return"0";var t=this.chunkSize(e),r=Math.pow(e,t),n=Pn(r),i=Be(),a=Be(),s="";for(this.divRemTo(n,i,a);i.signum()>0;)s=(r+a.intValue()).toString(e).substr(1)+s,i.divRemTo(n,i,a);return a.intValue().toString(e)+s}function VE(e,t){this.fromInt(0),t==null&&(t=10);for(var r=this.chunkSize(t),n=Math.pow(t,r),i=!1,a=0,s=0,o=0;o=r&&(this.dMultiply(n),this.dAddOffset(s,0),a=0,s=0)}a>0&&(this.dMultiply(Math.pow(t,a)),this.dAddOffset(s,0)),i&&K.ZERO.subTo(this,this)}function jE(e,t,r){if(typeof t=="number")if(e<2)this.fromInt(1);else for(this.fromNumber(e,r),this.testBit(e-1)||this.bitwiseTo(K.ONE.shiftLeft(e-1),s0,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(t);)this.dAddOffset(2,0),this.bitLength()>e&&this.subTo(K.ONE.shiftLeft(e-1),this);else{var n=new Array,i=e&7;n.length=(e>>3)+1,t.nextBytes(n),i>0?n[0]&=(1<0)for(r>r)!=(this.s&this.DM)>>r&&(t[i++]=n|this.s<=0;)r<8?(n=(this.data[e]&(1<>(r+=this.DB-8)):(n=this.data[e]>>(r-=8)&255,r<=0&&(r+=this.DB,--e)),n&128&&(n|=-256),i==0&&(this.s&128)!=(n&128)&&++i,(i>0||n!=this.s)&&(t[i++]=n);return t}function zE(e){return this.compareTo(e)==0}function HE(e){return this.compareTo(e)<0?this:e}function qE(e){return this.compareTo(e)>0?this:e}function $E(e,t,r){var n,i,a=Math.min(e.t,this.t);for(n=0;n>=16,t+=16),e&255||(e>>=8,t+=8),e&15||(e>>=4,t+=4),e&3||(e>>=2,t+=2),e&1||++t,t}function rS(){for(var e=0;e=this.t?this.s!=0:(this.data[t]&1<>=this.DB;if(e.t>=this.DB;n+=this.s}else{for(n+=this.s;r>=this.DB;n+=e.s}t.s=n<0?-1:0,n>0?t.data[r++]=n:n<-1&&(t.data[r++]=this.DV+n),t.t=r,t.clamp()}function fS(e){var t=Be();return this.addTo(e,t),t}function dS(e){var t=Be();return this.subTo(e,t),t}function hS(e){var t=Be();return this.multiplyTo(e,t),t}function pS(e){var t=Be();return this.divRemTo(e,t,null),t}function gS(e){var t=Be();return this.divRemTo(e,null,t),t}function vS(e){var t=Be(),r=Be();return this.divRemTo(e,t,r),new Array(t,r)}function yS(e){this.data[this.t]=this.am(0,e-1,this,0,0,this.t),++this.t,this.clamp()}function mS(e,t){if(e!=0){for(;this.t<=t;)this.data[this.t++]=0;for(this.data[t]+=e;this.data[t]>=this.DV;)this.data[t]-=this.DV,++t>=this.t&&(this.data[this.t++]=0),++this.data[t]}}function ks(){}function A1(e){return e}function CS(e,t,r){e.multiplyTo(t,r)}function ES(e,t){e.squareTo(t)}ks.prototype.convert=A1;ks.prototype.revert=A1;ks.prototype.mulTo=CS;ks.prototype.sqrTo=ES;function SS(e){return this.exp(e,new ks)}function xS(e,t,r){var n=Math.min(this.t+e.t,t);for(r.s=0,r.t=n;n>0;)r.data[--n]=0;var i;for(i=r.t-this.t;n=0;)r.data[n]=0;for(n=Math.max(t-this.t,0);n2*this.m.t)return e.mod(this.m);if(e.compareTo(this.m)<0)return e;var t=Be();return e.copyTo(t),this.reduce(t),t}function IS(e){return e}function _S(e){for(e.drShiftTo(this.m.t-1,this.r2),e.t>this.m.t+1&&(e.t=this.m.t+1,e.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);e.compareTo(this.r2)<0;)e.dAddOffset(1,this.m.t+1);for(e.subTo(this.r2,e);e.compareTo(this.m)>=0;)e.subTo(this.m,e)}function AS(e,t){e.squareTo(t),this.reduce(t)}function BS(e,t,r){e.multiplyTo(t,r),this.reduce(r)}ma.prototype.convert=wS;ma.prototype.revert=IS;ma.prototype.reduce=_S;ma.prototype.mulTo=BS;ma.prototype.sqrTo=AS;function kS(e,t){var r=e.bitLength(),n,i=Pn(1),a;if(r<=0)return i;r<18?n=1:r<48?n=3:r<144?n=4:r<768?n=5:n=6,r<8?a=new Ii(t):t.isEven()?a=new ma(t):a=new _i(t);var s=new Array,o=3,l=n-1,u=(1<1){var c=Be();for(a.sqrTo(s[1],c);o<=u;)s[o]=Be(),a.mulTo(c,s[o-2],s[o]),o+=2}var f=e.t-1,d,v=!0,g=Be(),C;for(r=Bl(e.data[f])-1;f>=0;){for(r>=l?d=e.data[f]>>r-l&u:(d=(e.data[f]&(1<0&&(d|=e.data[f-1]>>this.DB+r-l)),o=n;!(d&1);)d>>=1,--o;if((r-=o)<0&&(r+=this.DB,--f),v)s[d].copyTo(i),v=!1;else{for(;o>1;)a.sqrTo(i,g),a.sqrTo(g,i),o-=2;o>0?a.sqrTo(i,g):(C=i,i=g,g=C),a.mulTo(g,s[d],i)}for(;f>=0&&!(e.data[f]&1<0&&(t.rShiftTo(a,t),r.rShiftTo(a,r));t.signum()>0;)(i=t.getLowestSetBit())>0&&t.rShiftTo(i,t),(i=r.getLowestSetBit())>0&&r.rShiftTo(i,r),t.compareTo(r)>=0?(t.subTo(r,t),t.rShiftTo(1,t)):(r.subTo(t,r),r.rShiftTo(1,r));return a>0&&r.lShiftTo(a,r),r}function NS(e){if(e<=0)return 0;var t=this.DV%e,r=this.s<0?e-1:0;if(this.t>0)if(t==0)r=this.data[0]%e;else for(var n=this.t-1;n>=0;--n)r=(t*r+this.data[n])%e;return r}function DS(e){var t=e.isEven();if(this.isEven()&&t||e.signum()==0)return K.ZERO;for(var r=e.clone(),n=this.clone(),i=Pn(1),a=Pn(0),s=Pn(0),o=Pn(1);r.signum()!=0;){for(;r.isEven();)r.rShiftTo(1,r),t?((!i.isEven()||!a.isEven())&&(i.addTo(this,i),a.subTo(e,a)),i.rShiftTo(1,i)):a.isEven()||a.subTo(e,a),a.rShiftTo(1,a);for(;n.isEven();)n.rShiftTo(1,n),t?((!s.isEven()||!o.isEven())&&(s.addTo(this,s),o.subTo(e,o)),s.rShiftTo(1,s)):o.isEven()||o.subTo(e,o),o.rShiftTo(1,o);r.compareTo(n)>=0?(r.subTo(n,r),t&&i.subTo(s,i),a.subTo(o,a)):(n.subTo(r,n),t&&s.subTo(i,s),o.subTo(a,o))}if(n.compareTo(K.ONE)!=0)return K.ZERO;if(o.compareTo(e)>=0)return o.subtract(e);if(o.signum()<0)o.addTo(e,o);else return o;return o.signum()<0?o.add(e):o}var Lr=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509],RS=(1<<26)/Lr[Lr.length-1];function LS(e){var t,r=this.abs();if(r.t==1&&r.data[0]<=Lr[Lr.length-1]){for(t=0;t=0);var o=a.modPow(n,this);if(o.compareTo(K.ONE)!=0&&o.compareTo(t)!=0){for(var l=1;l++>>0,s>>>0];for(var o=n.fullMessageLength.length-1;o>=0;--o)n.fullMessageLength[o]+=s[1],s[1]=s[0]+(n.fullMessageLength[o]/4294967296>>>0),n.fullMessageLength[o]=n.fullMessageLength[o]>>>0,s[0]=s[1]/4294967296>>>0;return t.putBytes(i),hh(e,r,t),(t.read>2048||t.length()===0)&&t.compact(),n},n.digest=function(){var i=Xr.util.createBuffer();i.putBytes(t.bytes());var a=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize,s=a&n.blockLength-1;i.putBytes(Uc.substr(0,n.blockLength-s));for(var o,l,u=n.fullMessageLength[0]*8,c=0;c>>0,u+=l,i.putInt32(u>>>0),u=o>>>0;i.putInt32(u);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4};hh(f,r,i);var d=Xr.util.createBuffer();return d.putInt32(f.h0),d.putInt32(f.h1),d.putInt32(f.h2),d.putInt32(f.h3),d.putInt32(f.h4),d},n};var Uc=null,k1=!1;function US(){Uc=String.fromCharCode(128),Uc+=Xr.util.fillString(String.fromCharCode(0),64),k1=!0}function hh(e,t,r){for(var n,i,a,s,o,l,u,c,f=r.length();f>=64;){for(i=e.h0,a=e.h1,s=e.h2,o=e.h3,l=e.h4,c=0;c<16;++c)n=r.getInt32(),t[c]=n,u=o^a&(s^o),n=(i<<5|i>>>27)+u+l+1518500249+n,l=o,o=s,s=(a<<30|a>>>2)>>>0,a=i,i=n;for(;c<20;++c)n=t[c-3]^t[c-8]^t[c-14]^t[c-16],n=n<<1|n>>>31,t[c]=n,u=o^a&(s^o),n=(i<<5|i>>>27)+u+l+1518500249+n,l=o,o=s,s=(a<<30|a>>>2)>>>0,a=i,i=n;for(;c<32;++c)n=t[c-3]^t[c-8]^t[c-14]^t[c-16],n=n<<1|n>>>31,t[c]=n,u=a^s^o,n=(i<<5|i>>>27)+u+l+1859775393+n,l=o,o=s,s=(a<<30|a>>>2)>>>0,a=i,i=n;for(;c<40;++c)n=t[c-6]^t[c-16]^t[c-28]^t[c-32],n=n<<2|n>>>30,t[c]=n,u=a^s^o,n=(i<<5|i>>>27)+u+l+1859775393+n,l=o,o=s,s=(a<<30|a>>>2)>>>0,a=i,i=n;for(;c<60;++c)n=t[c-6]^t[c-16]^t[c-28]^t[c-32],n=n<<2|n>>>30,t[c]=n,u=a&s|o&(a^s),n=(i<<5|i>>>27)+u+l+2400959708+n,l=o,o=s,s=(a<<30|a>>>2)>>>0,a=i,i=n;for(;c<80;++c)n=t[c-6]^t[c-16]^t[c-28]^t[c-32],n=n<<2|n>>>30,t[c]=n,u=a^s^o,n=(i<<5|i>>>27)+u+l+3395469782+n,l=o,o=s,s=(a<<30|a>>>2)>>>0,a=i,i=n;e.h0=e.h0+i|0,e.h1=e.h1+a|0,e.h2=e.h2+s|0,e.h3=e.h3+o|0,e.h4=e.h4+l|0,f-=64}}var Jr=me,b1=Jr.pkcs1=Jr.pkcs1||{};b1.encode_rsa_oaep=function(e,t,r){var n,i,a,s;typeof r=="string"?(n=r,i=arguments[3]||void 0,a=arguments[4]||void 0):r&&(n=r.label||void 0,i=r.seed||void 0,a=r.md||void 0,r.mgf1&&r.mgf1.md&&(s=r.mgf1.md)),a?a.start():a=Jr.md.sha1.create(),s||(s=a);var o=Math.ceil(e.n.bitLength()/8),l=o-2*a.digestLength-2;if(t.length>l){var u=new Error("RSAES-OAEP input message length is too long.");throw u.length=t.length,u.maxLength=l,u}n||(n=""),a.update(n,"raw");for(var c=a.digest(),f="",d=l-t.length,v=0;v>24&255,a>>16&255,a>>8&255,a&255);r.start(),r.update(e+s),n+=r.digest().getBytes()}return n.substring(0,t)}var In=me;(function(){if(In.prime){In.prime;return}var e=In.prime=In.prime||{},t=In.jsbn.BigInteger,r=[6,4,2,4,2,4,6,2],n=new t(null);n.fromInt(30);var i=function(f,d){return f|d};e.generateProbablePrime=function(f,d,v){typeof d=="function"&&(v=d,d={}),d=d||{};var g=d.algorithm||"PRIMEINC";typeof g=="string"&&(g={name:g}),g.options=g.options||{};var C=d.prng||In.random,I={nextBytes:function(y){for(var m=C.getBytesSync(y.length),S=0;Sd&&(f=u(d,v)),f.isProbablePrime(C))return y(null,f);f.dAddOffset(r[g++%8],0)}while(I<0||+new Date-m"u")return s(f,d,v,g);var C=u(f,d),I=v.workers,y=v.workLoad||100,m=y*30/8,S=v.workerScript||"forge/prime.worker.js";if(I===-1)return In.util.estimateCores(function(R,L){R&&(L=2),I=L-1,B()});B();function B(){I=Math.max(1,I);for(var R=[],L=0;Lf&&(C=u(f,d));var pe=C.toString(16);Q.target.postMessage({hex:pe,workLoad:y}),C.dAddOffset(m,0)}}}}function u(f,d){var v=new t(f,d),g=f-1;return v.testBit(g)||v.bitwiseTo(t.ONE.shiftLeft(g),i,v),v.dAddOffset(31-v.mod(n).byteValue(),0),v}function c(f){return f<=100?27:f<=150?18:f<=200?15:f<=250?12:f<=300?9:f<=350?8:f<=400?7:f<=500?6:f<=600?5:f<=800?4:f<=1250?3:2}})();var oe=me;if(typeof _e>"u")var _e=oe.jsbn.BigInteger;var Mc=oe.util.isNodejs?i0:null,O=oe.asn1,_r=oe.util;oe.pki=oe.pki||{};oe.pki.rsa=oe.rsa=oe.rsa||{};var he=oe.pki,MS=[6,4,2,4,2,4,6,2],FS={name:"PrivateKeyInfo",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,value:[{name:"PrivateKeyInfo.version",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:O.Class.UNIVERSAL,type:O.Type.OID,constructed:!1,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:O.Class.UNIVERSAL,type:O.Type.OCTETSTRING,constructed:!1,capture:"privateKey"}]},VS={name:"RSAPrivateKey",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPrivateKey.version",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"RSAPrivateKey.modulus",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyModulus"},{name:"RSAPrivateKey.publicExponent",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyPublicExponent"},{name:"RSAPrivateKey.privateExponent",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyPrivateExponent"},{name:"RSAPrivateKey.prime1",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyPrime1"},{name:"RSAPrivateKey.prime2",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyPrime2"},{name:"RSAPrivateKey.exponent1",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyExponent1"},{name:"RSAPrivateKey.exponent2",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyExponent2"},{name:"RSAPrivateKey.coefficient",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"privateKeyCoefficient"}]},jS={name:"RSAPublicKey",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPublicKey.modulus",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"publicKeyModulus"},{name:"RSAPublicKey.exponent",tagClass:O.Class.UNIVERSAL,type:O.Type.INTEGER,constructed:!1,capture:"publicKeyExponent"}]},KS=oe.pki.rsa.publicKeyValidator={name:"SubjectPublicKeyInfo",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:O.Class.UNIVERSAL,type:O.Type.OID,constructed:!1,capture:"publicKeyOid"}]},{name:"SubjectPublicKeyInfo.subjectPublicKey",tagClass:O.Class.UNIVERSAL,type:O.Type.BITSTRING,constructed:!1,value:[{name:"SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,optional:!0,captureAsn1:"rsaPublicKey"}]}]},zS={name:"DigestInfo",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm",tagClass:O.Class.UNIVERSAL,type:O.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm.algorithmIdentifier",tagClass:O.Class.UNIVERSAL,type:O.Type.OID,constructed:!1,capture:"algorithmIdentifier"},{name:"DigestInfo.DigestAlgorithm.parameters",tagClass:O.Class.UNIVERSAL,type:O.Type.NULL,capture:"parameters",optional:!0,constructed:!1}]},{name:"DigestInfo.digest",tagClass:O.Class.UNIVERSAL,type:O.Type.OCTETSTRING,constructed:!1,capture:"digest"}]},HS=function(e){var t;if(e.algorithm in he.oids)t=he.oids[e.algorithm];else{var r=new Error("Unknown message digest algorithm.");throw r.algorithm=e.algorithm,r}var n=O.oidToDer(t).getBytes(),i=O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[]),a=O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[]);a.value.push(O.create(O.Class.UNIVERSAL,O.Type.OID,!1,n)),a.value.push(O.create(O.Class.UNIVERSAL,O.Type.NULL,!1,""));var s=O.create(O.Class.UNIVERSAL,O.Type.OCTETSTRING,!1,e.digest().getBytes());return i.value.push(a),i.value.push(s),O.toDer(i).getBytes()},N1=function(e,t,r){if(r)return e.modPow(t.e,t.n);if(!t.p||!t.q)return e.modPow(t.d,t.n);t.dP||(t.dP=t.d.mod(t.p.subtract(_e.ONE))),t.dQ||(t.dQ=t.d.mod(t.q.subtract(_e.ONE))),t.qInv||(t.qInv=t.q.modInverse(t.p));var n;do n=new _e(oe.util.bytesToHex(oe.random.getBytes(t.n.bitLength()/8)),16);while(n.compareTo(t.n)>=0||!n.gcd(t.n).equals(_e.ONE));e=e.multiply(n.modPow(t.e,t.n)).mod(t.n);for(var i=e.mod(t.p).modPow(t.dP,t.p),a=e.mod(t.q).modPow(t.dQ,t.q);i.compareTo(a)<0;)i=i.add(t.p);var s=i.subtract(a).multiply(t.qInv).mod(t.p).multiply(t.q).add(a);return s=s.multiply(n.modInverse(t.n)).mod(t.n),s};he.rsa.encrypt=function(e,t,r){var n=r,i,a=Math.ceil(t.n.bitLength()/8);r!==!1&&r!==!0?(n=r===2,i=D1(e,t,r)):(i=oe.util.createBuffer(),i.putBytes(e));for(var s=new _e(i.toHex(),16),o=N1(s,t,n),l=o.toString(16),u=oe.util.createBuffer(),c=a-Math.ceil(l.length/2);c>0;)u.putByte(0),--c;return u.putBytes(oe.util.hexToBytes(l)),u.getBytes()};he.rsa.decrypt=function(e,t,r,n){var i=Math.ceil(t.n.bitLength()/8);if(e.length!==i){var a=new Error("Encrypted message length is invalid.");throw a.length=e.length,a.expected=i,a}var s=new _e(oe.util.createBuffer(e).toHex(),16);if(s.compareTo(t.n)>=0)throw new Error("Encrypted message is invalid.");for(var o=N1(s,t,r),l=o.toString(16),u=oe.util.createBuffer(),c=i-Math.ceil(l.length/2);c>0;)u.putByte(0),--c;return u.putBytes(oe.util.hexToBytes(l)),n!==!1?Xo(u.getBytes(),t,r):u.getBytes()};he.rsa.createKeyPairGenerationState=function(e,t,r){typeof e=="string"&&(e=parseInt(e,10)),e=e||2048,r=r||{};var n=r.prng||oe.random,i={nextBytes:function(o){for(var l=n.getBytesSync(o.length),u=0;u>1,pBits:e-(e>>1),pqState:0,num:null,keys:null},s.e.fromInt(s.eInt);else throw new Error("Invalid key generation algorithm: "+a);return s};he.rsa.stepKeyPairGenerationState=function(e,t){"algorithm"in e||(e.algorithm="PRIMEINC");var r=new _e(null);r.fromInt(30);for(var n=0,i=function(f,d){return f|d},a=+new Date,s,o=0;e.keys===null&&(t<=0||ol?e.pqState=0:e.num.isProbablePrime($S(e.num.bitLength()))?++e.pqState:e.num.dAddOffset(MS[n++%8],0):e.pqState===2?e.pqState=e.num.subtract(_e.ONE).gcd(e.e).compareTo(_e.ONE)===0?3:0:e.pqState===3&&(e.pqState=0,e.p===null?e.p=e.num:e.q=e.num,e.p!==null&&e.q!==null&&++e.state,e.num=null)}else if(e.state===1)e.p.compareTo(e.q)<0&&(e.num=e.p,e.p=e.q,e.q=e.num),++e.state;else if(e.state===2)e.p1=e.p.subtract(_e.ONE),e.q1=e.q.subtract(_e.ONE),e.phi=e.p1.multiply(e.q1),++e.state;else if(e.state===3)e.phi.gcd(e.e).compareTo(_e.ONE)===0?++e.state:(e.p=null,e.q=null,e.state=0);else if(e.state===4)e.n=e.p.multiply(e.q),e.n.bitLength()===e.bits?++e.state:(e.q=null,e.state=0);else if(e.state===5){var c=e.e.modInverse(e.phi);e.keys={privateKey:he.rsa.setPrivateKey(e.n,e.e,c,e.p,e.q,c.mod(e.p1),c.mod(e.q1),e.q.modInverse(e.p)),publicKey:he.rsa.setPublicKey(e.n,e.e)}}s=+new Date,o+=s-a,a=s}return e.keys!==null};he.rsa.generateKeyPair=function(e,t,r,n){if(arguments.length===1?typeof e=="object"?(r=e,e=void 0):typeof e=="function"&&(n=e,e=void 0):arguments.length===2?typeof e=="number"?typeof t=="function"?(n=t,t=void 0):typeof t!="number"&&(r=t,t=void 0):(r=e,n=t,e=void 0,t=void 0):arguments.length===3&&(typeof t=="number"?typeof r=="function"&&(n=r,r=void 0):(n=r,r=t,t=void 0)),r=r||{},e===void 0&&(e=r.bits||2048),t===void 0&&(t=r.e||65537),!oe.options.usePureJavaScript&&!r.prng&&e>=256&&e<=16384&&(t===65537||t===3)){if(n){if(ph("generateKeyPair"))return Mc.generateKeyPair("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}},function(o,l,u){if(o)return n(o);n(null,{privateKey:he.privateKeyFromPem(u),publicKey:he.publicKeyFromPem(l)})});if(gh("generateKey")&&gh("exportKey"))return _r.globalScope.crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:yh(t),hash:{name:"SHA-256"}},!0,["sign","verify"]).then(function(o){return _r.globalScope.crypto.subtle.exportKey("pkcs8",o.privateKey)}).then(void 0,function(o){n(o)}).then(function(o){if(o){var l=he.privateKeyFromAsn1(O.fromDer(oe.util.createBuffer(o)));n(null,{privateKey:l,publicKey:he.setRsaPublicKey(l.n,l.e)})}});if(vh("generateKey")&&vh("exportKey")){var i=_r.globalScope.msCrypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:yh(t),hash:{name:"SHA-256"}},!0,["sign","verify"]);i.oncomplete=function(o){var l=o.target.result,u=_r.globalScope.msCrypto.subtle.exportKey("pkcs8",l.privateKey);u.oncomplete=function(c){var f=c.target.result,d=he.privateKeyFromAsn1(O.fromDer(oe.util.createBuffer(f)));n(null,{privateKey:d,publicKey:he.setRsaPublicKey(d.n,d.e)})},u.onerror=function(c){n(c)}},i.onerror=function(o){n(o)};return}}else if(ph("generateKeyPairSync")){var a=Mc.generateKeyPairSync("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}});return{privateKey:he.privateKeyFromPem(a.privateKey),publicKey:he.publicKeyFromPem(a.publicKey)}}}var s=he.rsa.createKeyPairGenerationState(e,t,r);if(!n)return he.rsa.stepKeyPairGenerationState(s,0),s.keys;qS(s,r,n)};he.setRsaPublicKey=he.rsa.setPublicKey=function(e,t){var r={n:e,e:t};return r.encrypt=function(n,i,a){if(typeof i=="string"?i=i.toUpperCase():i===void 0&&(i="RSAES-PKCS1-V1_5"),i==="RSAES-PKCS1-V1_5")i={encode:function(o,l,u){return D1(o,l,2).getBytes()}};else if(i==="RSA-OAEP"||i==="RSAES-OAEP")i={encode:function(o,l){return oe.pkcs1.encode_rsa_oaep(l,o,a)}};else if(["RAW","NONE","NULL",null].indexOf(i)!==-1)i={encode:function(o){return o}};else if(typeof i=="string")throw new Error('Unsupported encryption scheme: "'+i+'".');var s=i.encode(n,r,!0);return he.rsa.encrypt(s,r,!0)},r.verify=function(n,i,a,s){typeof a=="string"?a=a.toUpperCase():a===void 0&&(a="RSASSA-PKCS1-V1_5"),s===void 0&&(s={_parseAllDigestBytes:!0}),"_parseAllDigestBytes"in s||(s._parseAllDigestBytes=!0),a==="RSASSA-PKCS1-V1_5"?a={verify:function(l,u){u=Xo(u,r,!0);var c=O.fromDer(u,{parseAllBytes:s._parseAllDigestBytes}),f={},d=[];if(!O.validate(c,zS,f,d)){var v=new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.");throw v.errors=d,v}var g=O.derToOid(f.algorithmIdentifier);if(!(g===oe.oids.md2||g===oe.oids.md5||g===oe.oids.sha1||g===oe.oids.sha224||g===oe.oids.sha256||g===oe.oids.sha384||g===oe.oids.sha512||g===oe.oids["sha512-224"]||g===oe.oids["sha512-256"])){var v=new Error("Unknown RSASSA-PKCS1-v1_5 DigestAlgorithm identifier.");throw v.oid=g,v}if((g===oe.oids.md2||g===oe.oids.md5)&&!("parameters"in f))throw new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value. Missing algorithm identifer NULL parameters.");return l===f.digest}}:(a==="NONE"||a==="NULL"||a===null)&&(a={verify:function(l,u){return u=Xo(u,r,!0),l===u}});var o=he.rsa.decrypt(i,r,!0,!1);return a.verify(n,o,r.n.bitLength())},r};he.setRsaPrivateKey=he.rsa.setPrivateKey=function(e,t,r,n,i,a,s,o){var l={n:e,e:t,d:r,p:n,q:i,dP:a,dQ:s,qInv:o};return l.decrypt=function(u,c,f){typeof c=="string"?c=c.toUpperCase():c===void 0&&(c="RSAES-PKCS1-V1_5");var d=he.rsa.decrypt(u,l,!1,!1);if(c==="RSAES-PKCS1-V1_5")c={decode:Xo};else if(c==="RSA-OAEP"||c==="RSAES-OAEP")c={decode:function(v,g){return oe.pkcs1.decode_rsa_oaep(g,v,f)}};else if(["RAW","NONE","NULL",null].indexOf(c)!==-1)c={decode:function(v){return v}};else throw new Error('Unsupported encryption scheme: "'+c+'".');return c.decode(d,l,!1)},l.sign=function(u,c){var f=!1;typeof c=="string"&&(c=c.toUpperCase()),c===void 0||c==="RSASSA-PKCS1-V1_5"?(c={encode:HS},f=1):(c==="NONE"||c==="NULL"||c===null)&&(c={encode:function(){return u}},f=1);var d=c.encode(u,l.n.bitLength());return he.rsa.encrypt(d,l,f)},l};he.wrapRsaPrivateKey=function(e){return O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,O.integerToDer(0).getBytes()),O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[O.create(O.Class.UNIVERSAL,O.Type.OID,!1,O.oidToDer(he.oids.rsaEncryption).getBytes()),O.create(O.Class.UNIVERSAL,O.Type.NULL,!1,"")]),O.create(O.Class.UNIVERSAL,O.Type.OCTETSTRING,!1,O.toDer(e).getBytes())])};he.privateKeyFromAsn1=function(e){var t={},r=[];if(O.validate(e,FS,t,r)&&(e=O.fromDer(oe.util.createBuffer(t.privateKey))),t={},r=[],!O.validate(e,VS,t,r)){var n=new Error("Cannot read private key. ASN.1 object does not contain an RSAPrivateKey.");throw n.errors=r,n}var i,a,s,o,l,u,c,f;return i=oe.util.createBuffer(t.privateKeyModulus).toHex(),a=oe.util.createBuffer(t.privateKeyPublicExponent).toHex(),s=oe.util.createBuffer(t.privateKeyPrivateExponent).toHex(),o=oe.util.createBuffer(t.privateKeyPrime1).toHex(),l=oe.util.createBuffer(t.privateKeyPrime2).toHex(),u=oe.util.createBuffer(t.privateKeyExponent1).toHex(),c=oe.util.createBuffer(t.privateKeyExponent2).toHex(),f=oe.util.createBuffer(t.privateKeyCoefficient).toHex(),he.setRsaPrivateKey(new _e(i,16),new _e(a,16),new _e(s,16),new _e(o,16),new _e(l,16),new _e(u,16),new _e(c,16),new _e(f,16))};he.privateKeyToAsn1=he.privateKeyToRSAPrivateKey=function(e){return O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,O.integerToDer(0).getBytes()),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.n)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.e)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.d)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.p)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.q)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.dP)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.dQ)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.qInv))])};he.publicKeyFromAsn1=function(e){var t={},r=[];if(O.validate(e,KS,t,r)){var n=O.derToOid(t.publicKeyOid);if(n!==he.oids.rsaEncryption){var i=new Error("Cannot read public key. Unknown OID.");throw i.oid=n,i}e=t.rsaPublicKey}if(r=[],!O.validate(e,jS,t,r)){var i=new Error("Cannot read public key. ASN.1 object does not contain an RSAPublicKey.");throw i.errors=r,i}var a=oe.util.createBuffer(t.publicKeyModulus).toHex(),s=oe.util.createBuffer(t.publicKeyExponent).toHex();return he.setRsaPublicKey(new _e(a,16),new _e(s,16))};he.publicKeyToAsn1=he.publicKeyToSubjectPublicKeyInfo=function(e){return O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[O.create(O.Class.UNIVERSAL,O.Type.OID,!1,O.oidToDer(he.oids.rsaEncryption).getBytes()),O.create(O.Class.UNIVERSAL,O.Type.NULL,!1,"")]),O.create(O.Class.UNIVERSAL,O.Type.BITSTRING,!1,[he.publicKeyToRSAPublicKey(e)])])};he.publicKeyToRSAPublicKey=function(e){return O.create(O.Class.UNIVERSAL,O.Type.SEQUENCE,!0,[O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.n)),O.create(O.Class.UNIVERSAL,O.Type.INTEGER,!1,qr(e.e))])};function D1(e,t,r){var n=oe.util.createBuffer(),i=Math.ceil(t.n.bitLength()/8);if(e.length>i-11){var a=new Error("Message is too long for PKCS#1 v1.5 padding.");throw a.length=e.length,a.max=i-11,a}n.putByte(0),n.putByte(r);var s=i-3-e.length,o;if(r===0||r===1){o=r===0?0:255;for(var l=0;l0;){for(var u=0,c=oe.random.getBytes(s),l=0;l"u")throw new Error("Encryption block is invalid.");var l=0;if(o===0){l=i-3-n;for(var u=0;u1;){if(a.getByte()!==255){--a.read;break}++l}else if(o===2)for(l=0;a.length()>1;){if(a.getByte()===0){--a.read;break}++l}var c=a.getByte();if(c!==0||l!==i-3-a.length())throw new Error("Encryption block is invalid.");return a.getBytes()}function qS(e,t,r){typeof t=="function"&&(r=t,t={}),t=t||{};var n={algorithm:{name:t.algorithm||"PRIMEINC",options:{workers:t.workers||2,workLoad:t.workLoad||100,workerScript:t.workerScript}}};"prng"in t&&(n.prng=t.prng),i();function i(){a(e.pBits,function(o,l){if(o)return r(o);if(e.p=l,e.q!==null)return s(o,e.q);a(e.qBits,s)})}function a(o,l){oe.prime.generateProbablePrime(o,n,l)}function s(o,l){if(o)return r(o);if(e.q=l,e.p.compareTo(e.q)<0){var u=e.p;e.p=e.q,e.q=u}if(e.p.subtract(_e.ONE).gcd(e.e).compareTo(_e.ONE)!==0){e.p=null,i();return}if(e.q.subtract(_e.ONE).gcd(e.e).compareTo(_e.ONE)!==0){e.q=null,a(e.qBits,s);return}if(e.p1=e.p.subtract(_e.ONE),e.q1=e.q.subtract(_e.ONE),e.phi=e.p1.multiply(e.q1),e.phi.gcd(e.e).compareTo(_e.ONE)!==0){e.p=e.q=null,i();return}if(e.n=e.p.multiply(e.q),e.n.bitLength()!==e.bits){e.q=null,a(e.qBits,s);return}var c=e.e.modInverse(e.phi);e.keys={privateKey:he.rsa.setPrivateKey(e.n,e.e,c,e.p,e.q,c.mod(e.p1),c.mod(e.q1),e.q.modInverse(e.p)),publicKey:he.rsa.setPublicKey(e.n,e.e)},r(null,e.keys)}}function qr(e){var t=e.toString(16);t[0]>="8"&&(t="00"+t);var r=oe.util.hexToBytes(t);return r.length>1&&(r.charCodeAt(0)===0&&!(r.charCodeAt(1)&128)||r.charCodeAt(0)===255&&(r.charCodeAt(1)&128)===128)?r.substr(1):r}function $S(e){return e<=100?27:e<=150?18:e<=200?15:e<=250?12:e<=300?9:e<=350?8:e<=400?7:e<=500?6:e<=600?5:e<=800?4:e<=1250?3:2}function ph(e){return oe.util.isNodejs&&typeof Mc[e]=="function"}function gh(e){return typeof _r.globalScope<"u"&&typeof _r.globalScope.crypto=="object"&&typeof _r.globalScope.crypto.subtle=="object"&&typeof _r.globalScope.crypto.subtle[e]=="function"}function vh(e){return typeof _r.globalScope<"u"&&typeof _r.globalScope.msCrypto=="object"&&typeof _r.globalScope.msCrypto.subtle=="object"&&typeof _r.globalScope.msCrypto.subtle[e]=="function"}function yh(e){for(var t=oe.util.hexToBytes(e.toString(16)),r=new Uint8Array(t.length),n=0;n"u")var GS=J.jsbn.BigInteger;var U=J.asn1,ye=J.pki=J.pki||{};ye.pbe=J.pbe=J.pbe||{};var ri=ye.oids,QS={name:"EncryptedPrivateKeyInfo",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedPrivateKeyInfo.encryptionAlgorithm",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:U.Class.UNIVERSAL,type:U.Type.OID,constructed:!1,capture:"encryptionOid"},{name:"AlgorithmIdentifier.parameters",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,captureAsn1:"encryptionParams"}]},{name:"EncryptedPrivateKeyInfo.encryptedData",tagClass:U.Class.UNIVERSAL,type:U.Type.OCTETSTRING,constructed:!1,capture:"encryptedData"}]},WS={name:"PBES2Algorithms",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.keyDerivationFunc",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.keyDerivationFunc.oid",tagClass:U.Class.UNIVERSAL,type:U.Type.OID,constructed:!1,capture:"kdfOid"},{name:"PBES2Algorithms.params",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.params.salt",tagClass:U.Class.UNIVERSAL,type:U.Type.OCTETSTRING,constructed:!1,capture:"kdfSalt"},{name:"PBES2Algorithms.params.iterationCount",tagClass:U.Class.UNIVERSAL,type:U.Type.INTEGER,constructed:!1,capture:"kdfIterationCount"},{name:"PBES2Algorithms.params.keyLength",tagClass:U.Class.UNIVERSAL,type:U.Type.INTEGER,constructed:!1,optional:!0,capture:"keyLength"},{name:"PBES2Algorithms.params.prf",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,optional:!0,value:[{name:"PBES2Algorithms.params.prf.algorithm",tagClass:U.Class.UNIVERSAL,type:U.Type.OID,constructed:!1,capture:"prfOid"}]}]}]},{name:"PBES2Algorithms.encryptionScheme",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,value:[{name:"PBES2Algorithms.encryptionScheme.oid",tagClass:U.Class.UNIVERSAL,type:U.Type.OID,constructed:!1,capture:"encOid"},{name:"PBES2Algorithms.encryptionScheme.iv",tagClass:U.Class.UNIVERSAL,type:U.Type.OCTETSTRING,constructed:!1,capture:"encIv"}]}]},YS={name:"pkcs-12PbeParams",tagClass:U.Class.UNIVERSAL,type:U.Type.SEQUENCE,constructed:!0,value:[{name:"pkcs-12PbeParams.salt",tagClass:U.Class.UNIVERSAL,type:U.Type.OCTETSTRING,constructed:!1,capture:"salt"},{name:"pkcs-12PbeParams.iterations",tagClass:U.Class.UNIVERSAL,type:U.Type.INTEGER,constructed:!1,capture:"iterations"}]};ye.encryptPrivateKeyInfo=function(e,t,r){r=r||{},r.saltSize=r.saltSize||8,r.count=r.count||2048,r.algorithm=r.algorithm||"aes128",r.prfAlgorithm=r.prfAlgorithm||"sha1";var n=J.random.getBytesSync(r.saltSize),i=r.count,a=U.integerToDer(i),s,o,l;if(r.algorithm.indexOf("aes")===0||r.algorithm==="des"){var u,c,f;switch(r.algorithm){case"aes128":s=16,u=16,c=ri["aes128-CBC"],f=J.aes.createEncryptionCipher;break;case"aes192":s=24,u=16,c=ri["aes192-CBC"],f=J.aes.createEncryptionCipher;break;case"aes256":s=32,u=16,c=ri["aes256-CBC"],f=J.aes.createEncryptionCipher;break;case"des":s=8,u=8,c=ri.desCBC,f=J.des.createEncryptionCipher;break;default:var d=new Error("Cannot encrypt private key. Unknown encryption algorithm.");throw d.algorithm=r.algorithm,d}var v="hmacWith"+r.prfAlgorithm.toUpperCase(),g=L1(v),C=J.pkcs5.pbkdf2(t,n,i,s,g),I=J.random.getBytesSync(u),y=f(C);y.start(I),y.update(U.toDer(e)),y.finish(),l=y.output.getBytes();var m=XS(n,a,s,v);o=U.create(U.Class.UNIVERSAL,U.Type.SEQUENCE,!0,[U.create(U.Class.UNIVERSAL,U.Type.OID,!1,U.oidToDer(ri.pkcs5PBES2).getBytes()),U.create(U.Class.UNIVERSAL,U.Type.SEQUENCE,!0,[U.create(U.Class.UNIVERSAL,U.Type.SEQUENCE,!0,[U.create(U.Class.UNIVERSAL,U.Type.OID,!1,U.oidToDer(ri.pkcs5PBKDF2).getBytes()),m]),U.create(U.Class.UNIVERSAL,U.Type.SEQUENCE,!0,[U.create(U.Class.UNIVERSAL,U.Type.OID,!1,U.oidToDer(c).getBytes()),U.create(U.Class.UNIVERSAL,U.Type.OCTETSTRING,!1,I)])])])}else if(r.algorithm==="3des"){s=24;var S=new J.util.ByteBuffer(n),C=ye.pbe.generatePkcs12Key(t,S,1,i,s),I=ye.pbe.generatePkcs12Key(t,S,2,i,s),y=J.des.createEncryptionCipher(C);y.start(I),y.update(U.toDer(e)),y.finish(),l=y.output.getBytes(),o=U.create(U.Class.UNIVERSAL,U.Type.SEQUENCE,!0,[U.create(U.Class.UNIVERSAL,U.Type.OID,!1,U.oidToDer(ri["pbeWithSHAAnd3-KeyTripleDES-CBC"]).getBytes()),U.create(U.Class.UNIVERSAL,U.Type.SEQUENCE,!0,[U.create(U.Class.UNIVERSAL,U.Type.OCTETSTRING,!1,n),U.create(U.Class.UNIVERSAL,U.Type.INTEGER,!1,a.getBytes())])])}else{var d=new Error("Cannot encrypt private key. Unknown encryption algorithm.");throw d.algorithm=r.algorithm,d}var B=U.create(U.Class.UNIVERSAL,U.Type.SEQUENCE,!0,[o,U.create(U.Class.UNIVERSAL,U.Type.OCTETSTRING,!1,l)]);return B};ye.decryptPrivateKeyInfo=function(e,t){var r=null,n={},i=[];if(!U.validate(e,QS,n,i)){var a=new Error("Cannot read encrypted private key. ASN.1 object is not a supported EncryptedPrivateKeyInfo.");throw a.errors=i,a}var s=U.derToOid(n.encryptionOid),o=ye.pbe.getCipher(s,n.encryptionParams,t),l=J.util.createBuffer(n.encryptedData);return o.update(l),o.finish()&&(r=U.fromDer(o.output)),r};ye.encryptedPrivateKeyToPem=function(e,t){var r={type:"ENCRYPTED PRIVATE KEY",body:U.toDer(e).getBytes()};return J.pem.encode(r,{maxline:t})};ye.encryptedPrivateKeyFromPem=function(e){var t=J.pem.decode(e)[0];if(t.type!=="ENCRYPTED PRIVATE KEY"){var r=new Error('Could not convert encrypted private key from PEM; PEM header type is "ENCRYPTED PRIVATE KEY".');throw r.headerType=t.type,r}if(t.procType&&t.procType.type==="ENCRYPTED")throw new Error("Could not convert encrypted private key from PEM; PEM is encrypted.");return U.fromDer(t.body)};ye.encryptRsaPrivateKey=function(e,t,r){if(r=r||{},!r.legacy){var n=ye.wrapRsaPrivateKey(ye.privateKeyToAsn1(e));return n=ye.encryptPrivateKeyInfo(n,t,r),ye.encryptedPrivateKeyToPem(n)}var i,a,s,o;switch(r.algorithm){case"aes128":i="AES-128-CBC",s=16,a=J.random.getBytesSync(16),o=J.aes.createEncryptionCipher;break;case"aes192":i="AES-192-CBC",s=24,a=J.random.getBytesSync(16),o=J.aes.createEncryptionCipher;break;case"aes256":i="AES-256-CBC",s=32,a=J.random.getBytesSync(16),o=J.aes.createEncryptionCipher;break;case"3des":i="DES-EDE3-CBC",s=24,a=J.random.getBytesSync(8),o=J.des.createEncryptionCipher;break;case"des":i="DES-CBC",s=8,a=J.random.getBytesSync(8),o=J.des.createEncryptionCipher;break;default:var l=new Error('Could not encrypt RSA private key; unsupported encryption algorithm "'+r.algorithm+'".');throw l.algorithm=r.algorithm,l}var u=J.pbe.opensslDeriveBytes(t,a.substr(0,8),s),c=o(u);c.start(a),c.update(U.toDer(ye.privateKeyToAsn1(e))),c.finish();var f={type:"RSA PRIVATE KEY",procType:{version:"4",type:"ENCRYPTED"},dekInfo:{algorithm:i,parameters:J.util.bytesToHex(a).toUpperCase()},body:c.output.getBytes()};return J.pem.encode(f)};ye.decryptRsaPrivateKey=function(e,t){var r=null,n=J.pem.decode(e)[0];if(n.type!=="ENCRYPTED PRIVATE KEY"&&n.type!=="PRIVATE KEY"&&n.type!=="RSA PRIVATE KEY"){var i=new Error('Could not convert private key from PEM; PEM header type is not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".');throw i.headerType=i,i}if(n.procType&&n.procType.type==="ENCRYPTED"){var a,s;switch(n.dekInfo.algorithm){case"DES-CBC":a=8,s=J.des.createDecryptionCipher;break;case"DES-EDE3-CBC":a=24,s=J.des.createDecryptionCipher;break;case"AES-128-CBC":a=16,s=J.aes.createDecryptionCipher;break;case"AES-192-CBC":a=24,s=J.aes.createDecryptionCipher;break;case"AES-256-CBC":a=32,s=J.aes.createDecryptionCipher;break;case"RC2-40-CBC":a=5,s=function(f){return J.rc2.createDecryptionCipher(f,40)};break;case"RC2-64-CBC":a=8,s=function(f){return J.rc2.createDecryptionCipher(f,64)};break;case"RC2-128-CBC":a=16,s=function(f){return J.rc2.createDecryptionCipher(f,128)};break;default:var i=new Error('Could not decrypt private key; unsupported encryption algorithm "'+n.dekInfo.algorithm+'".');throw i.algorithm=n.dekInfo.algorithm,i}var o=J.util.hexToBytes(n.dekInfo.parameters),l=J.pbe.opensslDeriveBytes(t,o.substr(0,8),a),u=s(l);if(u.start(o),u.update(J.util.createBuffer(n.body)),u.finish())r=u.output.getBytes();else return r}else r=n.body;return n.type==="ENCRYPTED PRIVATE KEY"?r=ye.decryptPrivateKeyInfo(U.fromDer(r),t):r=U.fromDer(r),r!==null&&(r=ye.privateKeyFromAsn1(r)),r};ye.pbe.generatePkcs12Key=function(e,t,r,n,i,a){var s,o;if(typeof a>"u"||a===null){if(!("sha1"in J.md))throw new Error('"sha1" hash algorithm unavailable.');a=J.md.sha1.create()}var l=a.digestLength,u=a.blockLength,c=new J.util.ByteBuffer,f=new J.util.ByteBuffer;if(e!=null){for(o=0;o=0;o--)pe=pe>>8,pe+=V.at(o)+ce.at(o),ce.setAt(o,pe&255);G.putBuffer(ce)}S=G,c.putBuffer(L)}return c.truncate(c.length()-i),c};ye.pbe.getCipher=function(e,t,r){switch(e){case ye.oids.pkcs5PBES2:return ye.pbe.getCipherForPBES2(e,t,r);case ye.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:case ye.oids["pbewithSHAAnd40BitRC2-CBC"]:return ye.pbe.getCipherForPKCS12PBE(e,t,r);default:var n=new Error("Cannot read encrypted PBE data block. Unsupported OID.");throw n.oid=e,n.supportedOids=["pkcs5PBES2","pbeWithSHAAnd3-KeyTripleDES-CBC","pbewithSHAAnd40BitRC2-CBC"],n}};ye.pbe.getCipherForPBES2=function(e,t,r){var n={},i=[];if(!U.validate(t,WS,n,i)){var a=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.");throw a.errors=i,a}if(e=U.derToOid(n.kdfOid),e!==ye.oids.pkcs5PBKDF2){var a=new Error("Cannot read encrypted private key. Unsupported key derivation function OID.");throw a.oid=e,a.supportedOids=["pkcs5PBKDF2"],a}if(e=U.derToOid(n.encOid),e!==ye.oids["aes128-CBC"]&&e!==ye.oids["aes192-CBC"]&&e!==ye.oids["aes256-CBC"]&&e!==ye.oids["des-EDE3-CBC"]&&e!==ye.oids.desCBC){var a=new Error("Cannot read encrypted private key. Unsupported encryption scheme OID.");throw a.oid=e,a.supportedOids=["aes128-CBC","aes192-CBC","aes256-CBC","des-EDE3-CBC","desCBC"],a}var s=n.kdfSalt,o=J.util.createBuffer(n.kdfIterationCount);o=o.getInt(o.length()<<3);var l,u;switch(ye.oids[e]){case"aes128-CBC":l=16,u=J.aes.createDecryptionCipher;break;case"aes192-CBC":l=24,u=J.aes.createDecryptionCipher;break;case"aes256-CBC":l=32,u=J.aes.createDecryptionCipher;break;case"des-EDE3-CBC":l=24,u=J.des.createDecryptionCipher;break;case"desCBC":l=8,u=J.des.createDecryptionCipher;break}var c=R1(n.prfOid),f=J.pkcs5.pbkdf2(r,s,o,l,c),d=n.encIv,v=u(f);return v.start(d),v};ye.pbe.getCipherForPKCS12PBE=function(e,t,r){var n={},i=[];if(!U.validate(t,YS,n,i)){var a=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.");throw a.errors=i,a}var s=J.util.createBuffer(n.salt),o=J.util.createBuffer(n.iterations);o=o.getInt(o.length()<<3);var l,u,c;switch(e){case ye.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:l=24,u=8,c=J.des.startDecrypting;break;case ye.oids["pbewithSHAAnd40BitRC2-CBC"]:l=5,u=8,c=function(C,I){var y=J.rc2.createDecryptionCipher(C,40);return y.start(I,null),y};break;default:var a=new Error("Cannot read PKCS #12 PBE data block. Unsupported OID.");throw a.oid=e,a}var f=R1(n.prfOid),d=ye.pbe.generatePkcs12Key(r,s,1,o,l,f);f.start();var v=ye.pbe.generatePkcs12Key(r,s,2,o,u,f);return c(d,v)};ye.pbe.opensslDeriveBytes=function(e,t,r,n){if(typeof n>"u"||n===null){if(!("md5"in J.md))throw new Error('"md5" hash algorithm unavailable.');n=J.md.md5.create()}t===null&&(t="");for(var i=[mh(n,e+t)],a=16,s=1;a>8*d-f&255;return R=String.fromCharCode(R.charCodeAt(0)&~L)+R.substr(1),R+I+String.fromCharCode(188)},o.verify=function(l,u,c){var f,d=c-1,v=Math.ceil(d/8);if(u=u.substr(-v),v>8*v-d&255;if(C.charCodeAt(0)&y)throw new Error("Bits beyond keysize not zero as expected.");var m=r.generate(I,g),S="";for(f=0;f2)throw new Error("Cannot read notBefore/notAfter validity times; more than two times were provided in the certificate.");if(l.length<2)throw new Error("Cannot read notBefore/notAfter validity times; they were not provided as either UTCTime or GeneralizedTime.");if(s.validity.notBefore=l[0],s.validity.notAfter=l[1],s.tbsCertificate=r.tbsCertificate,t){s.md=kl({signatureOid:s.signatureOid,type:"certificate"});var u=E.toDer(s.tbsCertificate);s.md.update(u.getBytes())}var c=le.md.sha1.create(),f=E.toDer(r.certIssuer);c.update(f.getBytes()),s.issuer.getField=function(g){return Xn(s.issuer,g)},s.issuer.addField=function(g){Ar([g]),s.issuer.attributes.push(g)},s.issuer.attributes=$.RDNAttributesAsArray(r.certIssuer),r.certIssuerUniqueId&&(s.issuer.uniqueId=r.certIssuerUniqueId),s.issuer.hash=c.digest().toHex();var d=le.md.sha1.create(),v=E.toDer(r.certSubject);return d.update(v.getBytes()),s.subject.getField=function(g){return Xn(s.subject,g)},s.subject.addField=function(g){Ar([g]),s.subject.attributes.push(g)},s.subject.attributes=$.RDNAttributesAsArray(r.certSubject),r.certSubjectUniqueId&&(s.subject.uniqueId=r.certSubjectUniqueId),s.subject.hash=d.digest().toHex(),r.certExtensions?s.extensions=$.certificateExtensionsFromAsn1(r.certExtensions):s.extensions=[],s.publicKey=$.publicKeyFromAsn1(r.subjectPublicKeyInfo),s};$.certificateExtensionsFromAsn1=function(e){for(var t=[],r=0;r1&&(n=r.value.charCodeAt(1),i=r.value.length>2?r.value.charCodeAt(2):0),t.digitalSignature=(n&128)===128,t.nonRepudiation=(n&64)===64,t.keyEncipherment=(n&32)===32,t.dataEncipherment=(n&16)===16,t.keyAgreement=(n&8)===8,t.keyCertSign=(n&4)===4,t.cRLSign=(n&2)===2,t.encipherOnly=(n&1)===1,t.decipherOnly=(i&128)===128}else if(t.name==="basicConstraints"){var r=E.fromDer(t.value);r.value.length>0&&r.value[0].type===E.Type.BOOLEAN?t.cA=r.value[0].value.charCodeAt(0)!==0:t.cA=!1;var a=null;r.value.length>0&&r.value[0].type===E.Type.INTEGER?a=r.value[0].value:r.value.length>1&&(a=r.value[1].value),a!==null&&(t.pathLenConstraint=E.derToInteger(a))}else if(t.name==="extKeyUsage")for(var r=E.fromDer(t.value),s=0;s1&&(n=r.value.charCodeAt(1)),t.client=(n&128)===128,t.server=(n&64)===64,t.email=(n&32)===32,t.objsign=(n&16)===16,t.reserved=(n&8)===8,t.sslCA=(n&4)===4,t.emailCA=(n&2)===2,t.objCA=(n&1)===1}else if(t.name==="subjectAltName"||t.name==="issuerAltName"){t.altNames=[];for(var l,r=E.fromDer(t.value),u=0;u"u"&&(t.type&&t.type in $.oids?t.name=$.oids[t.type]:t.shortName&&t.shortName in ut&&(t.name=$.oids[ut[t.shortName]])),typeof t.type>"u")if(t.name&&t.name in $.oids)t.type=$.oids[t.name];else{var n=new Error("Attribute type not specified.");throw n.attribute=t,n}if(typeof t.shortName>"u"&&t.name&&t.name in ut&&(t.shortName=ut[t.name]),t.type===Ie.extensionRequest&&(t.valueConstructed=!0,t.valueTagClass=E.Type.SEQUENCE,!t.value&&t.extensions)){t.value=[];for(var i=0;i"u"){var n=new Error("Attribute value not specified.");throw n.attribute=t,n}}}function F1(e,t){if(t=t||{},typeof e.name>"u"&&e.id&&e.id in $.oids&&(e.name=$.oids[e.id]),typeof e.id>"u")if(e.name&&e.name in $.oids)e.id=$.oids[e.name];else{var r=new Error("Extension ID not specified.");throw r.extension=e,r}if(typeof e.value<"u")return e;if(e.name==="keyUsage"){var n=0,i=0,a=0;e.digitalSignature&&(i|=128,n=7),e.nonRepudiation&&(i|=64,n=6),e.keyEncipherment&&(i|=32,n=5),e.dataEncipherment&&(i|=16,n=4),e.keyAgreement&&(i|=8,n=3),e.keyCertSign&&(i|=4,n=2),e.cRLSign&&(i|=2,n=1),e.encipherOnly&&(i|=1,n=0),e.decipherOnly&&(a|=128,n=7);var s=String.fromCharCode(n);a!==0?s+=String.fromCharCode(i)+String.fromCharCode(a):i!==0&&(s+=String.fromCharCode(i)),e.value=E.create(E.Class.UNIVERSAL,E.Type.BITSTRING,!1,s)}else if(e.name==="basicConstraints")e.value=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[]),e.cA&&e.value.value.push(E.create(E.Class.UNIVERSAL,E.Type.BOOLEAN,!1,String.fromCharCode(255))),"pathLenConstraint"in e&&e.value.value.push(E.create(E.Class.UNIVERSAL,E.Type.INTEGER,!1,E.integerToDer(e.pathLenConstraint).getBytes()));else if(e.name==="extKeyUsage"){e.value=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[]);var o=e.value.value;for(var l in e)e[l]===!0&&(l in Ie?o.push(E.create(E.Class.UNIVERSAL,E.Type.OID,!1,E.oidToDer(Ie[l]).getBytes())):l.indexOf(".")!==-1&&o.push(E.create(E.Class.UNIVERSAL,E.Type.OID,!1,E.oidToDer(l).getBytes())))}else if(e.name==="nsCertType"){var n=0,i=0;e.client&&(i|=128,n=7),e.server&&(i|=64,n=6),e.email&&(i|=32,n=5),e.objsign&&(i|=16,n=4),e.reserved&&(i|=8,n=3),e.sslCA&&(i|=4,n=2),e.emailCA&&(i|=2,n=1),e.objCA&&(i|=1,n=0);var s=String.fromCharCode(n);i!==0&&(s+=String.fromCharCode(i)),e.value=E.create(E.Class.UNIVERSAL,E.Type.BITSTRING,!1,s)}else if(e.name==="subjectAltName"||e.name==="issuerAltName"){e.value=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[]);for(var u,c=0;c128)throw new Error('Invalid "nsComment" content.');e.value=E.create(E.Class.UNIVERSAL,E.Type.IA5STRING,!1,e.comment)}else if(e.name==="subjectKeyIdentifier"&&t.cert){var f=t.cert.generateSubjectKeyIdentifier();e.subjectKeyIdentifier=f.toHex(),e.value=E.create(E.Class.UNIVERSAL,E.Type.OCTETSTRING,!1,f.getBytes())}else if(e.name==="authorityKeyIdentifier"&&t.cert){e.value=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[]);var o=e.value.value;if(e.keyIdentifier){var d=e.keyIdentifier===!0?t.cert.generateSubjectKeyIdentifier().getBytes():e.keyIdentifier;o.push(E.create(E.Class.CONTEXT_SPECIFIC,0,!1,d))}if(e.authorityCertIssuer){var v=[E.create(E.Class.CONTEXT_SPECIFIC,4,!0,[ca(e.authorityCertIssuer===!0?t.cert.issuer:e.authorityCertIssuer)])];o.push(E.create(E.Class.CONTEXT_SPECIFIC,1,!0,v))}if(e.serialNumber){var g=le.util.hexToBytes(e.serialNumber===!0?t.cert.serialNumber:e.serialNumber);o.push(E.create(E.Class.CONTEXT_SPECIFIC,2,!1,g))}}else if(e.name==="cRLDistributionPoints"){e.value=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[]);for(var o=e.value.value,C=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[]),I=E.create(E.Class.CONTEXT_SPECIFIC,0,!0,[]),u,c=0;c"u"){var r=new Error("Extension value not specified.");throw r.extension=e,r}return e}function o0(e,t){switch(e){case Ie["RSASSA-PSS"]:var r=[];return t.hash.algorithmOid!==void 0&&r.push(E.create(E.Class.CONTEXT_SPECIFIC,0,!0,[E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[E.create(E.Class.UNIVERSAL,E.Type.OID,!1,E.oidToDer(t.hash.algorithmOid).getBytes()),E.create(E.Class.UNIVERSAL,E.Type.NULL,!1,"")])])),t.mgf.algorithmOid!==void 0&&r.push(E.create(E.Class.CONTEXT_SPECIFIC,1,!0,[E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[E.create(E.Class.UNIVERSAL,E.Type.OID,!1,E.oidToDer(t.mgf.algorithmOid).getBytes()),E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[E.create(E.Class.UNIVERSAL,E.Type.OID,!1,E.oidToDer(t.mgf.hash.algorithmOid).getBytes()),E.create(E.Class.UNIVERSAL,E.Type.NULL,!1,"")])])])),t.saltLength!==void 0&&r.push(E.create(E.Class.CONTEXT_SPECIFIC,2,!0,[E.create(E.Class.UNIVERSAL,E.Type.INTEGER,!1,E.integerToDer(t.saltLength).getBytes())])),E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,r);default:return E.create(E.Class.UNIVERSAL,E.Type.NULL,!1,"")}}function ax(e){var t=E.create(E.Class.CONTEXT_SPECIFIC,0,!0,[]);if(e.attributes.length===0)return t;for(var r=e.attributes,n=0;n=sx&&e0&&n.value.push($.certificateExtensionsToAsn1(e.extensions)),n};$.getCertificationRequestInfo=function(e){var t=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[E.create(E.Class.UNIVERSAL,E.Type.INTEGER,!1,E.integerToDer(e.version).getBytes()),ca(e.subject),$.publicKeyToAsn1(e.publicKey),ax(e)]);return t};$.distinguishedNameToAsn1=function(e){return ca(e)};$.certificateToAsn1=function(e){var t=e.tbsCertificate||$.getTBSCertificate(e);return E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[t,E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[E.create(E.Class.UNIVERSAL,E.Type.OID,!1,E.oidToDer(e.signatureOid).getBytes()),o0(e.signatureOid,e.signatureParameters)]),E.create(E.Class.UNIVERSAL,E.Type.BITSTRING,!1,String.fromCharCode(0)+e.signature)])};$.certificateExtensionsToAsn1=function(e){var t=E.create(E.Class.CONTEXT_SPECIFIC,3,!0,[]),r=E.create(E.Class.UNIVERSAL,E.Type.SEQUENCE,!0,[]);t.value.push(r);for(var n=0;n"u"&&(i=new Date);var a=!0,s=null,o=0;do{var l=t.shift(),u=null,c=!1;if(i&&(il.validity.notAfter)&&(s={message:"Certificate is not valid yet or has expired.",error:$.certificateError.certificate_expired,notBefore:l.validity.notBefore,notAfter:l.validity.notAfter,now:i}),s===null){if(u=t[0]||e.getIssuer(l),u===null&&l.isIssuer(l)&&(c=!0,u=l),u){var f=u;le.util.isArray(f)||(f=[f]);for(var d=!1;!d&&f.length>0;){u=f.shift();try{d=u.verify(l)}catch{}}d||(s={message:"Certificate signature is invalid.",error:$.certificateError.bad_certificate})}s===null&&(!u||c)&&!e.hasCertificate(l)&&(s={message:"Certificate is not trusted.",error:$.certificateError.unknown_ca})}if(s===null&&u&&!l.isIssuer(u)&&(s={message:"Certificate issuer is invalid.",error:$.certificateError.bad_certificate}),s===null)for(var v={keyUsage:!0,basicConstraints:!0},g=0;s===null&&gI.pathLenConstraint&&(s={message:"Certificate basicConstraints pathLenConstraint violated.",error:$.certificateError.bad_certificate})}}var S=s===null?!0:s.error,B=r.verify?r.verify(S,o,n):S;if(B===!0)s=null;else throw S===!0&&(s={message:"The application rejected the certificate.",error:$.certificateError.bad_certificate}),(B||B===0)&&(typeof B=="object"&&!le.util.isArray(B)?(B.message&&(s.message=B.message),B.error&&(s.error=B.error)):typeof B=="string"&&(s.error=B)),s;a=!1,++o}while(t.length>0);return!0};var qe=me,A=qe.asn1,Ee=qe.pki,Ss=qe.pkcs12=qe.pkcs12||{},V1={name:"ContentInfo",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,value:[{name:"ContentInfo.contentType",tagClass:A.Class.UNIVERSAL,type:A.Type.OID,constructed:!1,capture:"contentType"},{name:"ContentInfo.content",tagClass:A.Class.CONTEXT_SPECIFIC,constructed:!0,captureAsn1:"content"}]},lx={name:"PFX",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.version",tagClass:A.Class.UNIVERSAL,type:A.Type.INTEGER,constructed:!1,capture:"version"},V1,{name:"PFX.macData",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,optional:!0,captureAsn1:"mac",value:[{name:"PFX.macData.mac",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.macData.mac.digestAlgorithm",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,value:[{name:"PFX.macData.mac.digestAlgorithm.algorithm",tagClass:A.Class.UNIVERSAL,type:A.Type.OID,constructed:!1,capture:"macAlgorithm"},{name:"PFX.macData.mac.digestAlgorithm.parameters",tagClass:A.Class.UNIVERSAL,captureAsn1:"macAlgorithmParameters"}]},{name:"PFX.macData.mac.digest",tagClass:A.Class.UNIVERSAL,type:A.Type.OCTETSTRING,constructed:!1,capture:"macDigest"}]},{name:"PFX.macData.macSalt",tagClass:A.Class.UNIVERSAL,type:A.Type.OCTETSTRING,constructed:!1,capture:"macSalt"},{name:"PFX.macData.iterations",tagClass:A.Class.UNIVERSAL,type:A.Type.INTEGER,constructed:!1,optional:!0,capture:"macIterations"}]}]},ux={name:"SafeBag",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,value:[{name:"SafeBag.bagId",tagClass:A.Class.UNIVERSAL,type:A.Type.OID,constructed:!1,capture:"bagId"},{name:"SafeBag.bagValue",tagClass:A.Class.CONTEXT_SPECIFIC,constructed:!0,captureAsn1:"bagValue"},{name:"SafeBag.bagAttributes",tagClass:A.Class.UNIVERSAL,type:A.Type.SET,constructed:!0,optional:!0,capture:"bagAttributes"}]},cx={name:"Attribute",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,value:[{name:"Attribute.attrId",tagClass:A.Class.UNIVERSAL,type:A.Type.OID,constructed:!1,capture:"oid"},{name:"Attribute.attrValues",tagClass:A.Class.UNIVERSAL,type:A.Type.SET,constructed:!0,capture:"values"}]},fx={name:"CertBag",tagClass:A.Class.UNIVERSAL,type:A.Type.SEQUENCE,constructed:!0,value:[{name:"CertBag.certId",tagClass:A.Class.UNIVERSAL,type:A.Type.OID,constructed:!1,capture:"certId"},{name:"CertBag.certValue",tagClass:A.Class.CONTEXT_SPECIFIC,constructed:!0,value:[{name:"CertBag.certValue[0]",tagClass:A.Class.UNIVERSAL,type:A.Class.OCTETSTRING,constructed:!1,capture:"cert"}]}]};function Ra(e,t,r,n){for(var i=[],a=0;a=0&&i.push(o)}}return i}Ss.pkcs12FromAsn1=function(e,t,r){typeof t=="string"?(r=t,t=!0):t===void 0&&(t=!0);var n={},i=[];if(!A.validate(e,lx,n,i)){var a=new Error("Cannot read PKCS#12 PFX. ASN.1 object is not an PKCS#12 PFX.");throw a.errors=a,a}var s={version:n.version.charCodeAt(0),safeContents:[],getBags:function(I){var y={},m;return"localKeyId"in I?m=I.localKeyId:"localKeyIdHex"in I&&(m=qe.util.hexToBytes(I.localKeyIdHex)),m===void 0&&!("friendlyName"in I)&&"bagType"in I&&(y[I.bagType]=Ra(s.safeContents,null,null,I.bagType)),m!==void 0&&(y.localKeyId=Ra(s.safeContents,"localKeyId",m,I.bagType)),"friendlyName"in I&&(y.friendlyName=Ra(s.safeContents,"friendlyName",I.friendlyName,I.bagType)),y},getBagsByFriendlyName:function(I,y){return Ra(s.safeContents,"friendlyName",I,y)},getBagsByLocalKeyId:function(I,y){return Ra(s.safeContents,"localKeyId",I,y)}};if(n.version.charCodeAt(0)!==3){var a=new Error("PKCS#12 PFX of version other than 3 not supported.");throw a.version=n.version.charCodeAt(0),a}if(A.derToOid(n.contentType)!==Ee.oids.data){var a=new Error("Only PKCS#12 PFX in password integrity mode supported.");throw a.oid=A.derToOid(n.contentType),a}var o=n.content.value[0];if(o.tagClass!==A.Class.UNIVERSAL||o.type!==A.Type.OCTETSTRING)throw new Error("PKCS#12 authSafe content data is not an OCTET STRING.");if(o=l0(o),n.mac){var l=null,u=0,c=A.derToOid(n.macAlgorithm);switch(c){case Ee.oids.sha1:l=qe.md.sha1.create(),u=20;break;case Ee.oids.sha256:l=qe.md.sha256.create(),u=32;break;case Ee.oids.sha384:l=qe.md.sha384.create(),u=48;break;case Ee.oids.sha512:l=qe.md.sha512.create(),u=64;break;case Ee.oids.md5:l=qe.md.md5.create(),u=16;break}if(l===null)throw new Error("PKCS#12 uses unsupported MAC algorithm: "+c);var f=new qe.util.ByteBuffer(n.macSalt),d="macIterations"in n?parseInt(qe.util.bytesToHex(n.macIterations),16):1,v=Ss.generateKey(r,f,3,d,u,l),g=qe.hmac.create();g.start(l,v),g.update(o.value);var C=g.getMac();if(C.getBytes()!==n.macDigest)throw new Error("PKCS#12 MAC could not be verified. Invalid password?")}return dx(s,o.value,t,r),s};function l0(e){if(e.composed||e.constructed){for(var t=qe.util.createBuffer(),r=0;r0&&(a=A.create(A.Class.UNIVERSAL,A.Type.SET,!0,l));var u=[],c=[];t!==null&&(qe.util.isArray(t)?c=t:c=[t]);for(var f=[],d=0;d0){var I=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,f),y=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.OID,!1,A.oidToDer(Ee.oids.data).getBytes()),A.create(A.Class.CONTEXT_SPECIFIC,0,!0,[A.create(A.Class.UNIVERSAL,A.Type.OCTETSTRING,!1,A.toDer(I).getBytes())])]);u.push(y)}var m=null;if(e!==null){var S=Ee.wrapRsaPrivateKey(Ee.privateKeyToAsn1(e));r===null?m=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.OID,!1,A.oidToDer(Ee.oids.keyBag).getBytes()),A.create(A.Class.CONTEXT_SPECIFIC,0,!0,[S]),a]):m=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.OID,!1,A.oidToDer(Ee.oids.pkcs8ShroudedKeyBag).getBytes()),A.create(A.Class.CONTEXT_SPECIFIC,0,!0,[Ee.encryptPrivateKeyInfo(S,r,n)]),a]);var B=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[m]),R=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.OID,!1,A.oidToDer(Ee.oids.data).getBytes()),A.create(A.Class.CONTEXT_SPECIFIC,0,!0,[A.create(A.Class.UNIVERSAL,A.Type.OCTETSTRING,!1,A.toDer(B).getBytes())])]);u.push(R)}var L=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,u),M;if(n.useMac){var o=qe.md.sha1.create(),V=new qe.util.ByteBuffer(qe.random.getBytes(n.saltSize)),Q=n.count,e=Ss.generateKey(r,V,3,Q,20),G=qe.hmac.create();G.start(o,e),G.update(A.toDer(L).getBytes());var ce=G.getMac();M=A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.OID,!1,A.oidToDer(Ee.oids.sha1).getBytes()),A.create(A.Class.UNIVERSAL,A.Type.NULL,!1,"")]),A.create(A.Class.UNIVERSAL,A.Type.OCTETSTRING,!1,ce.getBytes())]),A.create(A.Class.UNIVERSAL,A.Type.OCTETSTRING,!1,V.getBytes()),A.create(A.Class.UNIVERSAL,A.Type.INTEGER,!1,A.integerToDer(Q).getBytes())])}return A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.INTEGER,!1,A.integerToDer(3).getBytes()),A.create(A.Class.UNIVERSAL,A.Type.SEQUENCE,!0,[A.create(A.Class.UNIVERSAL,A.Type.OID,!1,A.oidToDer(Ee.oids.data).getBytes()),A.create(A.Class.CONTEXT_SPECIFIC,0,!0,[A.create(A.Class.UNIVERSAL,A.Type.OCTETSTRING,!1,A.toDer(L).getBytes())])]),M])};Ss.generateKey=qe.pbe.generatePkcs12Key;var Zn=me,u0=Zn.asn1,fa=Zn.pki=Zn.pki||{};fa.pemToDer=function(e){var t=Zn.pem.decode(e)[0];if(t.procType&&t.procType.type==="ENCRYPTED")throw new Error("Could not convert PEM to DER; PEM is encrypted.");return Zn.util.createBuffer(t.body)};fa.privateKeyFromPem=function(e){var t=Zn.pem.decode(e)[0];if(t.type!=="PRIVATE KEY"&&t.type!=="RSA PRIVATE KEY"){var r=new Error('Could not convert private key from PEM; PEM header type is not "PRIVATE KEY" or "RSA PRIVATE KEY".');throw r.headerType=t.type,r}if(t.procType&&t.procType.type==="ENCRYPTED")throw new Error("Could not convert private key from PEM; PEM is encrypted.");var n=u0.fromDer(t.body);return fa.privateKeyFromAsn1(n)};fa.privateKeyToPem=function(e,t){var r={type:"RSA PRIVATE KEY",body:u0.toDer(fa.privateKeyToAsn1(e)).getBytes()};return Zn.pem.encode(r,{maxline:t})};fa.privateKeyInfoToPem=function(e,t){var r={type:"PRIVATE KEY",body:u0.toDer(e).getBytes()};return Zn.pem.encode(r,{maxline:t})};var j=me,bl=function(e,t,r,n){var i=j.util.createBuffer(),a=e.length>>1,s=a+(e.length&1),o=e.substr(0,s),l=e.substr(a,s),u=j.util.createBuffer(),c=j.hmac.create();r=t+r;var f=Math.ceil(n/16),d=Math.ceil(n/20);c.start("MD5",o);var v=j.util.createBuffer();u.putBytes(r);for(var g=0;g0&&(T.queue(e,T.createAlert(e,{level:T.Alert.Level.warning,description:T.Alert.Description.no_renegotiation})),T.flush(e)),e.process()};T.parseHelloMessage=function(e,t,r){var n=null,i=e.entity===T.ConnectionEnd.client;if(r<38)e.error(e,{message:i?"Invalid ServerHello message. Message too short.":"Invalid ClientHello message. Message too short.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.illegal_parameter}});else{var a=t.fragment,s=a.length();if(n={version:{major:a.getByte(),minor:a.getByte()},random:j.util.createBuffer(a.getBytes(32)),session_id:sr(a,1),extensions:[]},i?(n.cipher_suite=a.getBytes(2),n.compression_method=a.getByte()):(n.cipher_suites=sr(a,2),n.compression_methods=sr(a,1)),s=r-(s-a.length()),s>0){for(var o=sr(a,2);o.length()>0;)n.extensions.push({type:[o.getByte(),o.getByte()],data:sr(o,2)});if(!i)for(var l=0;l0;){var f=c.getByte();if(f!==0)break;e.session.extensions.server_name.serverNameList.push(sr(c,2).getBytes())}}}if(e.session.version&&(n.version.major!==e.session.version.major||n.version.minor!==e.session.version.minor))return e.error(e,{message:"TLS version change is disallowed during renegotiation.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.protocol_version}});if(i)e.session.cipherSuite=T.getCipherSuite(n.cipher_suite);else for(var d=j.util.createBuffer(n.cipher_suites.bytes());d.length()>0&&(e.session.cipherSuite=T.getCipherSuite(d.getBytes(2)),e.session.cipherSuite===null););if(e.session.cipherSuite===null)return e.error(e,{message:"No cipher suites in common.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.handshake_failure},cipherSuite:j.util.bytesToHex(n.cipher_suite)});i?e.session.compressionMethod=n.compression_method:e.session.compressionMethod=T.CompressionMethod.none}return n};T.createSecurityParameters=function(e,t){var r=e.entity===T.ConnectionEnd.client,n=t.random.bytes(),i=r?e.session.sp.client_random:n,a=r?n:T.createRandom().getBytes();e.session.sp={entity:e.entity,prf_algorithm:T.PRFAlgorithm.tls_prf_sha256,bulk_cipher_algorithm:null,cipher_type:null,enc_key_length:null,block_length:null,fixed_iv_length:null,record_iv_length:null,mac_algorithm:null,mac_length:null,mac_key_length:null,compression_algorithm:e.session.compressionMethod,pre_master_secret:null,master_secret:null,client_random:i,server_random:a}};T.handleServerHello=function(e,t,r){var n=T.parseHelloMessage(e,t,r);if(!e.fail){if(n.version.minor<=e.version.minor)e.version.minor=n.version.minor;else return e.error(e,{message:"Incompatible TLS version.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.protocol_version}});e.session.version=e.version;var i=n.session_id.bytes();i.length>0&&i===e.session.id?(e.expect=j1,e.session.resuming=!0,e.session.sp.server_random=n.random.bytes()):(e.expect=Ex,e.session.resuming=!1,T.createSecurityParameters(e,n)),e.session.id=i,e.process()}};T.handleClientHello=function(e,t,r){var n=T.parseHelloMessage(e,t,r);if(!e.fail){var i=n.session_id.bytes(),a=null;if(e.sessionCache&&(a=e.sessionCache.getSession(i),a===null?i="":(a.version.major!==n.version.major||a.version.minor>n.version.minor)&&(a=null,i="")),i.length===0&&(i=j.random.getBytes(32)),e.session.id=i,e.session.clientHelloVersion=n.version,e.session.sp={},a)e.version=e.session.version=a.version,e.session.sp=a.sp;else{for(var s,o=1;o0;)a=sr(i.certificate_list,3),s=j.asn1.fromDer(a),a=j.pki.certificateFromAsn1(s,!0),o.push(a)}catch(u){return e.error(e,{message:"Could not parse certificate list.",cause:u,send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.bad_certificate}})}var l=e.entity===T.ConnectionEnd.client;(l||e.verifyClient===!0)&&o.length===0?e.error(e,{message:l?"No server certificate provided.":"No client certificate provided.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.illegal_parameter}}):o.length===0?e.expect=l?Eh:Fc:(l?e.session.serverCertificate=o[0]:e.session.clientCertificate=o[0],T.verifyCertificateChain(e,o)&&(e.expect=l?Eh:Fc)),e.process()};T.handleServerKeyExchange=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.unsupported_certificate}});e.expect=Sx,e.process()};T.handleClientKeyExchange=function(e,t,r){if(r<48)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.unsupported_certificate}});var n=t.fragment,i={enc_pre_master_secret:sr(n,2).getBytes()},a=null;if(e.getPrivateKey)try{a=e.getPrivateKey(e,e.session.serverCertificate),a=j.pki.privateKeyFromPem(a)}catch(l){e.error(e,{message:"Could not get private key.",cause:l,send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.internal_error}})}if(a===null)return e.error(e,{message:"No private key set.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.internal_error}});try{var s=e.session.sp;s.pre_master_secret=a.decrypt(i.enc_pre_master_secret);var o=e.session.clientHelloVersion;if(o.major!==s.pre_master_secret.charCodeAt(0)||o.minor!==s.pre_master_secret.charCodeAt(1))throw new Error("TLS version rollback attack detected.")}catch{s.pre_master_secret=j.random.getBytes(48)}e.expect=c0,e.session.clientCertificate!==null&&(e.expect=Bx),e.process()};T.handleCertificateRequest=function(e,t,r){if(r<3)return e.error(e,{message:"Invalid CertificateRequest. Message too short.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.illegal_parameter}});var n=t.fragment,i={certificate_types:sr(n,1),certificate_authorities:sr(n,2)};e.session.certificateRequest=i,e.expect=xx,e.process()};T.handleCertificateVerify=function(e,t,r){if(r<2)return e.error(e,{message:"Invalid CertificateVerify. Message too short.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.illegal_parameter}});var n=t.fragment;n.read-=4;var i=n.bytes();n.read+=4;var a={signature:sr(n,2).getBytes()},s=j.util.createBuffer();s.putBuffer(e.session.md5.digest()),s.putBuffer(e.session.sha1.digest()),s=s.getBytes();try{var o=e.session.clientCertificate;if(!o.publicKey.verify(s,a.signature,"NONE"))throw new Error("CertificateVerify signature does not match.");e.session.md5.update(i),e.session.sha1.update(i)}catch{return e.error(e,{message:"Bad signature in CertificateVerify.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.handshake_failure}})}e.expect=c0,e.process()};T.handleServerHelloDone=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid ServerHelloDone message. Invalid length.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.record_overflow}});if(e.serverCertificate===null){var n={message:"No server certificate provided. Not enough security.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.insufficient_security}},i=0,a=e.verify(e,n.alert.description,i,[]);if(a!==!0)return(a||a===0)&&(typeof a=="object"&&!j.util.isArray(a)?(a.message&&(n.message=a.message),a.alert&&(n.alert.description=a.alert)):typeof a=="number"&&(n.alert.description=a)),e.error(e,n)}e.session.certificateRequest!==null&&(t=T.createRecord(e,{type:T.ContentType.handshake,data:T.createCertificate(e)}),T.queue(e,t)),t=T.createRecord(e,{type:T.ContentType.handshake,data:T.createClientKeyExchange(e)}),T.queue(e,t),e.expect=Ix;var s=function(o,l){o.session.certificateRequest!==null&&o.session.clientCertificate!==null&&T.queue(o,T.createRecord(o,{type:T.ContentType.handshake,data:T.createCertificateVerify(o,l)})),T.queue(o,T.createRecord(o,{type:T.ContentType.change_cipher_spec,data:T.createChangeCipherSpec()})),o.state.pending=T.createConnectionState(o),o.state.current.write=o.state.pending.write,T.queue(o,T.createRecord(o,{type:T.ContentType.handshake,data:T.createFinished(o)})),o.expect=j1,T.flush(o),o.process()};if(e.session.certificateRequest===null||e.session.clientCertificate===null)return s(e,null);T.getClientSignature(e,s)};T.handleChangeCipherSpec=function(e,t){if(t.fragment.getByte()!==1)return e.error(e,{message:"Invalid ChangeCipherSpec message received.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.illegal_parameter}});var r=e.entity===T.ConnectionEnd.client;(e.session.resuming&&r||!e.session.resuming&&!r)&&(e.state.pending=T.createConnectionState(e)),e.state.current.read=e.state.pending.read,(!e.session.resuming&&r||e.session.resuming&&!r)&&(e.state.pending=null),e.expect=r?Tx:kx,e.process()};T.handleFinished=function(e,t,r){var n=t.fragment;n.read-=4;var i=n.bytes();n.read+=4;var a=t.fragment.getBytes();n=j.util.createBuffer(),n.putBuffer(e.session.md5.digest()),n.putBuffer(e.session.sha1.digest());var s=e.entity===T.ConnectionEnd.client,o=s?"server finished":"client finished",l=e.session.sp,u=12,c=bl;if(n=c(l.master_secret,o,n.getBytes(),u),n.getBytes()!==a)return e.error(e,{message:"Invalid verify_data in Finished message.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.decrypt_error}});e.session.md5.update(i),e.session.sha1.update(i),(e.session.resuming&&s||!e.session.resuming&&!s)&&(T.queue(e,T.createRecord(e,{type:T.ContentType.change_cipher_spec,data:T.createChangeCipherSpec()})),e.state.current.write=e.state.pending.write,e.state.pending=null,T.queue(e,T.createRecord(e,{type:T.ContentType.handshake,data:T.createFinished(e)}))),e.expect=s?wx:bx,e.handshaking=!1,++e.handshakes,e.peerCertificate=s?e.session.serverCertificate:e.session.clientCertificate,T.flush(e),e.isConnected=!0,e.connected(e),e.process()};T.handleAlert=function(e,t){var r=t.fragment,n={level:r.getByte(),description:r.getByte()},i;switch(n.description){case T.Alert.Description.close_notify:i="Connection closed.";break;case T.Alert.Description.unexpected_message:i="Unexpected message.";break;case T.Alert.Description.bad_record_mac:i="Bad record MAC.";break;case T.Alert.Description.decryption_failed:i="Decryption failed.";break;case T.Alert.Description.record_overflow:i="Record overflow.";break;case T.Alert.Description.decompression_failure:i="Decompression failed.";break;case T.Alert.Description.handshake_failure:i="Handshake failure.";break;case T.Alert.Description.bad_certificate:i="Bad certificate.";break;case T.Alert.Description.unsupported_certificate:i="Unsupported certificate.";break;case T.Alert.Description.certificate_revoked:i="Certificate revoked.";break;case T.Alert.Description.certificate_expired:i="Certificate expired.";break;case T.Alert.Description.certificate_unknown:i="Certificate unknown.";break;case T.Alert.Description.illegal_parameter:i="Illegal parameter.";break;case T.Alert.Description.unknown_ca:i="Unknown certificate authority.";break;case T.Alert.Description.access_denied:i="Access denied.";break;case T.Alert.Description.decode_error:i="Decode error.";break;case T.Alert.Description.decrypt_error:i="Decrypt error.";break;case T.Alert.Description.export_restriction:i="Export restriction.";break;case T.Alert.Description.protocol_version:i="Unsupported protocol version.";break;case T.Alert.Description.insufficient_security:i="Insufficient security.";break;case T.Alert.Description.internal_error:i="Internal error.";break;case T.Alert.Description.user_canceled:i="User canceled.";break;case T.Alert.Description.no_renegotiation:i="Renegotiation not supported.";break;default:i="Unknown error.";break}if(n.description===T.Alert.Description.close_notify)return e.close();e.error(e,{message:i,send:!1,origin:e.entity===T.ConnectionEnd.client?"server":"client",alert:n}),e.process()};T.handleHandshake=function(e,t){var r=t.fragment,n=r.getByte(),i=r.getInt24();if(i>r.length())return e.fragmented=t,t.fragment=j.util.createBuffer(),r.read-=4,e.process();e.fragmented=null,r.read-=4;var a=r.bytes(i+4);r.read+=4,n in el[e.entity][e.expect]?(e.entity===T.ConnectionEnd.server&&!e.open&&!e.fail&&(e.handshaking=!0,e.session={version:null,extensions:{server_name:{serverNameList:[]}},cipherSuite:null,compressionMethod:null,serverCertificate:null,clientCertificate:null,md5:j.md.md5.create(),sha1:j.md.sha1.create()}),n!==T.HandshakeType.hello_request&&n!==T.HandshakeType.certificate_verify&&n!==T.HandshakeType.finished&&(e.session.md5.update(a),e.session.sha1.update(a)),el[e.entity][e.expect][n](e,t,i)):T.handleUnexpected(e,t)};T.handleApplicationData=function(e,t){e.data.putBuffer(t.fragment),e.dataReady(e),e.process()};T.handleHeartbeat=function(e,t){var r=t.fragment,n=r.getByte(),i=r.getInt16(),a=r.getBytes(i);if(n===T.HeartbeatMessageType.heartbeat_request){if(e.handshaking||i>a.length)return e.process();T.queue(e,T.createRecord(e,{type:T.ContentType.heartbeat,data:T.createHeartbeat(T.HeartbeatMessageType.heartbeat_response,a)})),T.flush(e)}else if(n===T.HeartbeatMessageType.heartbeat_response){if(a!==e.expectedHeartbeatPayload)return e.process();e.heartbeatReceived&&e.heartbeatReceived(e,j.util.createBuffer(a))}e.process()};var Cx=0,Ex=1,Eh=2,Sx=3,xx=4,j1=5,Tx=6,wx=7,Ix=8,_x=0,Ax=1,Fc=2,Bx=3,c0=4,kx=5,bx=6,x=T.handleUnexpected,K1=T.handleChangeCipherSpec,kt=T.handleAlert,Kt=T.handleHandshake,z1=T.handleApplicationData,bt=T.handleHeartbeat,f0=[];f0[T.ConnectionEnd.client]=[[x,kt,Kt,x,bt],[x,kt,Kt,x,bt],[x,kt,Kt,x,bt],[x,kt,Kt,x,bt],[x,kt,Kt,x,bt],[K1,kt,x,x,bt],[x,kt,Kt,x,bt],[x,kt,Kt,z1,bt],[x,kt,Kt,x,bt]];f0[T.ConnectionEnd.server]=[[x,kt,Kt,x,bt],[x,kt,Kt,x,bt],[x,kt,Kt,x,bt],[x,kt,Kt,x,bt],[K1,kt,x,x,bt],[x,kt,Kt,x,bt],[x,kt,Kt,z1,bt],[x,kt,Kt,x,bt]];var _n=T.handleHelloRequest,Nx=T.handleServerHello,H1=T.handleCertificate,Sh=T.handleServerKeyExchange,xu=T.handleCertificateRequest,to=T.handleServerHelloDone,q1=T.handleFinished,el=[];el[T.ConnectionEnd.client]=[[x,x,Nx,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x],[_n,x,x,x,x,x,x,x,x,x,x,H1,Sh,xu,to,x,x,x,x,x,x],[_n,x,x,x,x,x,x,x,x,x,x,x,Sh,xu,to,x,x,x,x,x,x],[_n,x,x,x,x,x,x,x,x,x,x,x,x,xu,to,x,x,x,x,x,x],[_n,x,x,x,x,x,x,x,x,x,x,x,x,x,to,x,x,x,x,x,x],[_n,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x],[_n,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,q1],[_n,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x],[_n,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]];var Dx=T.handleClientHello,Rx=T.handleClientKeyExchange,Lx=T.handleCertificateVerify;el[T.ConnectionEnd.server]=[[x,Dx,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x],[x,x,x,x,x,x,x,x,x,x,x,H1,x,x,x,x,x,x,x,x,x],[x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,Rx,x,x,x,x],[x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,Lx,x,x,x,x,x],[x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x],[x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,q1],[x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x],[x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]];T.generateKeys=function(e,t){var r=bl,n=t.client_random+t.server_random;e.session.resuming||(t.master_secret=r(t.pre_master_secret,"master secret",n,48).bytes(),t.pre_master_secret=null),n=t.server_random+t.client_random;var i=2*t.mac_key_length+2*t.enc_key_length,a=e.version.major===T.Versions.TLS_1_0.major&&e.version.minor===T.Versions.TLS_1_0.minor;a&&(i+=2*t.fixed_iv_length);var s=r(t.master_secret,"key expansion",n,i),o={client_write_MAC_key:s.getBytes(t.mac_key_length),server_write_MAC_key:s.getBytes(t.mac_key_length),client_write_key:s.getBytes(t.enc_key_length),server_write_key:s.getBytes(t.enc_key_length)};return a&&(o.client_write_IV=s.getBytes(t.fixed_iv_length),o.server_write_IV=s.getBytes(t.fixed_iv_length)),o};T.createConnectionState=function(e){var t=e.entity===T.ConnectionEnd.client,r=function(){var a={sequenceNumber:[0,0],macKey:null,macLength:0,macFunction:null,cipherState:null,cipherFunction:function(s){return!0},compressionState:null,compressFunction:function(s){return!0},updateSequenceNumber:function(){a.sequenceNumber[1]===4294967295?(a.sequenceNumber[1]=0,++a.sequenceNumber[0]):++a.sequenceNumber[1]}};return a},n={read:r(),write:r()};if(n.read.update=function(a,s){return n.read.cipherFunction(s,n.read)?n.read.compressFunction(a,s,n.read)||a.error(a,{message:"Could not decompress record.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.decompression_failure}}):a.error(a,{message:"Could not decrypt record or bad MAC.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.bad_record_mac}}),!a.fail},n.write.update=function(a,s){return n.write.compressFunction(a,s,n.write)?n.write.cipherFunction(s,n.write)||a.error(a,{message:"Could not encrypt record.",send:!1,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.internal_error}}):a.error(a,{message:"Could not compress record.",send:!1,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.internal_error}}),!a.fail},e.session){var i=e.session.sp;switch(e.session.cipherSuite.initSecurityParameters(i),i.keys=T.generateKeys(e,i),n.read.macKey=t?i.keys.server_write_MAC_key:i.keys.client_write_MAC_key,n.write.macKey=t?i.keys.client_write_MAC_key:i.keys.server_write_MAC_key,e.session.cipherSuite.initConnectionState(n,e,i),i.compression_algorithm){case T.CompressionMethod.none:break;case T.CompressionMethod.deflate:n.read.compressFunction=mx,n.write.compressFunction=yx;break;default:throw new Error("Unsupported compression algorithm.")}}return n};T.createRandom=function(){var e=new Date,t=+e+e.getTimezoneOffset()*6e4,r=j.util.createBuffer();return r.putInt32(t),r.putBytes(j.random.getBytes(28)),r};T.createRecord=function(e,t){if(!t.data)return null;var r={type:t.type,version:{major:e.version.major,minor:e.version.minor},length:t.data.length(),fragment:t.data};return r};T.createAlert=function(e,t){var r=j.util.createBuffer();return r.putByte(t.level),r.putByte(t.description),T.createRecord(e,{type:T.ContentType.alert,data:r})};T.createClientHello=function(e){e.session.clientHelloVersion={major:e.version.major,minor:e.version.minor};for(var t=j.util.createBuffer(),r=0;r0&&(f+=2);var d=e.session.id,v=d.length+1+2+4+28+2+i+1+s+f,g=j.util.createBuffer();return g.putByte(T.HandshakeType.client_hello),g.putInt24(v),g.putByte(e.version.major),g.putByte(e.version.minor),g.putBytes(e.session.sp.client_random),Er(g,1,j.util.createBuffer(d)),Er(g,2,t),Er(g,1,a),f>0&&Er(g,2,o),g};T.createServerHello=function(e){var t=e.session.id,r=t.length+1+2+4+28+2+1,n=j.util.createBuffer();return n.putByte(T.HandshakeType.server_hello),n.putInt24(r),n.putByte(e.version.major),n.putByte(e.version.minor),n.putBytes(e.session.sp.server_random),Er(n,1,j.util.createBuffer(t)),n.putByte(e.session.cipherSuite.id[0]),n.putByte(e.session.cipherSuite.id[1]),n.putByte(e.session.compressionMethod),n};T.createCertificate=function(e){var t=e.entity===T.ConnectionEnd.client,r=null;if(e.getCertificate){var n;t?n=e.session.certificateRequest:n=e.session.extensions.server_name.serverNameList,r=e.getCertificate(e,n)}var i=j.util.createBuffer();if(r!==null)try{j.util.isArray(r)||(r=[r]);for(var a=null,s=0;s"u"&&(r=t.length);var n=j.util.createBuffer();n.putByte(e),n.putInt16(r),n.putBytes(t);var i=n.length(),a=Math.max(16,i-r-3);return n.putBytes(j.random.getBytes(a)),n};T.queue=function(e,t){if(t&&!(t.fragment.length()===0&&(t.type===T.ContentType.handshake||t.type===T.ContentType.alert||t.type===T.ContentType.change_cipher_spec))){if(t.type===T.ContentType.handshake){var r=t.fragment.bytes();e.session.md5.update(r),e.session.sha1.update(r),r=null}var n;if(t.fragment.length()<=T.MaxFragment)n=[t];else{n=[];for(var i=t.fragment.bytes();i.length>T.MaxFragment;)n.push(T.createRecord(e,{type:t.type,data:j.util.createBuffer(i.slice(0,T.MaxFragment))})),i=i.slice(T.MaxFragment);i.length>0&&n.push(T.createRecord(e,{type:t.type,data:j.util.createBuffer(i)}))}for(var a=0;a0&&(s=r.order[0]),s!==null&&s in r.cache){a=r.cache[s],delete r.cache[s];for(var o in r.order)if(r.order[o]===s){r.order.splice(o,1);break}}return a},r.setSession=function(i,a){if(r.order.length===r.capacity){var s=r.order.shift();delete r.cache[s]}var s=j.util.bytesToHex(i);r.order.push(s),r.cache[s]=a}}return r};T.createConnection=function(e){var t=null;e.caStore?j.util.isArray(e.caStore)?t=j.pki.createCaStore(e.caStore):t=e.caStore:t=j.pki.createCaStore();var r=e.cipherSuites||null;if(r===null){r=[];for(var n in T.CipherSuites)r.push(T.CipherSuites[n])}var i=e.server?T.ConnectionEnd.server:T.ConnectionEnd.client,a=e.sessionCache?T.createSessionCache(e.sessionCache):null,s={version:{major:T.Version.major,minor:T.Version.minor},entity:i,sessionId:e.sessionId,caStore:t,sessionCache:a,cipherSuites:r,connected:e.connected,virtualHost:e.virtualHost||null,verifyClient:e.verifyClient||!1,verify:e.verify||function(c,f,d,v){return f},verifyOptions:e.verifyOptions||{},getCertificate:e.getCertificate||null,getPrivateKey:e.getPrivateKey||null,getSignature:e.getSignature||null,input:j.util.createBuffer(),tlsData:j.util.createBuffer(),data:j.util.createBuffer(),tlsDataReady:e.tlsDataReady,dataReady:e.dataReady,heartbeatReceived:e.heartbeatReceived,closed:e.closed,error:function(c,f){f.origin=f.origin||(c.entity===T.ConnectionEnd.client?"client":"server"),f.send&&(T.queue(c,T.createAlert(c,f.alert)),T.flush(c));var d=f.fatal!==!1;d&&(c.fail=!0),e.error(c,f),d&&c.close(!1)},deflate:e.deflate||null,inflate:e.inflate||null};s.reset=function(c){s.version={major:T.Version.major,minor:T.Version.minor},s.record=null,s.session=null,s.peerCertificate=null,s.state={pending:null,current:null},s.expect=s.entity===T.ConnectionEnd.client?Cx:_x,s.fragmented=null,s.records=[],s.open=!1,s.handshakes=0,s.handshaking=!1,s.isConnected=!1,s.fail=!(c||typeof c>"u"),s.input.clear(),s.tlsData.clear(),s.data.clear(),s.state.current=T.createConnectionState(s)},s.reset();var o=function(c,f){var d=f.type-T.ContentType.change_cipher_spec,v=f0[c.entity][c.expect];d in v?v[d](c,f):T.handleUnexpected(c,f)},l=function(c){var f=0,d=c.input,v=d.length();if(v<5)f=5-v;else{c.record={type:d.getByte(),version:{major:d.getByte(),minor:d.getByte()},length:d.getInt16(),fragment:j.util.createBuffer(),ready:!1};var g=c.record.version.major===c.version.major;g&&c.session&&c.session.version&&(g=c.record.version.minor===c.version.minor),g||c.error(c,{message:"Incompatible TLS version.",send:!0,alert:{level:T.Alert.Level.fatal,description:T.Alert.Description.protocol_version}})}return f},u=function(c){var f=0,d=c.input,v=d.length();if(v0&&(s.sessionCache&&(f=s.sessionCache.getSession(c)),f===null&&(c="")),c.length===0&&s.sessionCache&&(f=s.sessionCache.getSession(),f!==null&&(c=f.id)),s.session={id:c,version:null,cipherSuite:null,compressionMethod:null,serverCertificate:null,certificateRequest:null,clientCertificate:null,sp:{},md5:j.md.md5.create(),sha1:j.md.sha1.create()},f&&(s.version=f.version,s.session.sp=f.sp),s.session.sp.client_random=T.createRandom().getBytes(),s.open=!0,T.queue(s,T.createRecord(s,{type:T.ContentType.handshake,data:T.createClientHello(s)})),T.flush(s)}},s.process=function(c){var f=0;return c&&s.input.putBytes(c),s.fail||(s.record!==null&&s.record.ready&&s.record.fragment.isEmpty()&&(s.record=null),s.record===null&&(f=l(s)),!s.fail&&s.record!==null&&!s.record.ready&&(f=u(s)),!s.fail&&s.record!==null&&s.record.ready&&o(s,s.record)),f},s.prepare=function(c){return T.queue(s,T.createRecord(s,{type:T.ContentType.application_data,data:j.util.createBuffer(c)})),T.flush(s)},s.prepareHeartbeatRequest=function(c,f){return c instanceof j.util.ByteBuffer&&(c=c.bytes()),typeof f>"u"&&(f=c.length),s.expectedHeartbeatPayload=c,T.queue(s,T.createRecord(s,{type:T.ContentType.heartbeat,data:T.createHeartbeat(T.HeartbeatMessageType.heartbeat_request,c,f)})),T.flush(s)},s.close=function(c){if(!s.fail&&s.sessionCache&&s.session){var f={id:s.session.id,version:s.session.version,sp:s.session.sp};f.sp.keys=null,s.sessionCache.setSession(f.id,f)}s.open&&(s.open=!1,s.input.clear(),(s.isConnected||s.handshaking)&&(s.isConnected=s.handshaking=!1,T.queue(s,T.createAlert(s,{level:T.Alert.Level.warning,description:T.Alert.Description.close_notify})),T.flush(s)),s.closed(s)),s.reset(c)},s};j.tls=j.tls||{};for(var wu in T)typeof T[wu]!="function"&&(j.tls[wu]=T[wu]);j.tls.prf_tls1=bl;j.tls.hmac_sha1=vx;j.tls.createSessionCache=T.createSessionCache;j.tls.createConnection=T.createConnection;var Qn=me,Br=Qn.tls;Br.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA={id:[0,47],name:"TLS_RSA_WITH_AES_128_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=Br.BulkCipherAlgorithm.aes,e.cipher_type=Br.CipherType.block,e.enc_key_length=16,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=Br.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:$1};Br.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA={id:[0,53],name:"TLS_RSA_WITH_AES_256_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=Br.BulkCipherAlgorithm.aes,e.cipher_type=Br.CipherType.block,e.enc_key_length=32,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=Br.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:$1};function $1(e,t,r){var n=t.entity===Qn.tls.ConnectionEnd.client;e.read.cipherState={init:!1,cipher:Qn.cipher.createDecipher("AES-CBC",n?r.keys.server_write_key:r.keys.client_write_key),iv:n?r.keys.server_write_IV:r.keys.client_write_IV},e.write.cipherState={init:!1,cipher:Qn.cipher.createCipher("AES-CBC",n?r.keys.client_write_key:r.keys.server_write_key),iv:n?r.keys.client_write_IV:r.keys.server_write_IV},e.read.cipherFunction=Fx,e.write.cipherFunction=Px,e.read.macLength=e.write.macLength=r.mac_length,e.read.macFunction=e.write.macFunction=Br.hmac_sha1}function Px(e,t){var r=!1,n=t.macFunction(t.macKey,t.sequenceNumber,e);e.fragment.putBytes(n),t.updateSequenceNumber();var i;e.version.minor===Br.Versions.TLS_1_0.minor?i=t.cipherState.init?null:t.cipherState.iv:i=Qn.random.getBytesSync(16),t.cipherState.init=!0;var a=t.cipherState.cipher;return a.start({iv:i}),e.version.minor>=Br.Versions.TLS_1_1.minor&&a.output.putBytes(i),a.update(e.fragment),a.finish(Ux)&&(e.fragment=a.output,e.length=e.fragment.length(),r=!0),r}function Ux(e,t,r){if(!r){var n=e-t.length()%e;t.fillWithByte(n-1,n)}return!0}function Mx(e,t,r){var n=!0;if(r){for(var i=t.length(),a=t.last(),s=i-1-a;s=a?(e.fragment=i.output.getBytes(o-a),s=i.output.getBytes(a)):e.fragment=i.output.getBytes(),e.fragment=Qn.util.createBuffer(e.fragment),e.length=e.fragment.length();var l=t.macFunction(t.macKey,t.sequenceNumber,e);return t.updateSequenceNumber(),r=Vx(t.macKey,s,l)&&r,r}function Vx(e,t,r){var n=Qn.hmac.create();return n.start("SHA1",e),n.update(t),t=n.digest().getBytes(),n.start(null,null),n.update(r),r=n.digest().getBytes(),t===r}var Je=me,bs=Je.sha512=Je.sha512||{};Je.md.sha512=Je.md.algorithms.sha512=bs;var G1=Je.sha384=Je.sha512.sha384=Je.sha512.sha384||{};G1.create=function(){return bs.create("SHA-384")};Je.md.sha384=Je.md.algorithms.sha384=G1;Je.sha512.sha256=Je.sha512.sha256||{create:function(){return bs.create("SHA-512/256")}};Je.md["sha512/256"]=Je.md.algorithms["sha512/256"]=Je.sha512.sha256;Je.sha512.sha224=Je.sha512.sha224||{create:function(){return bs.create("SHA-512/224")}};Je.md["sha512/224"]=Je.md.algorithms["sha512/224"]=Je.sha512.sha224;bs.create=function(e){if(Q1||jx(),typeof e>"u"&&(e="SHA-512"),!(e in si))throw new Error("Invalid SHA-512 algorithm: "+e);for(var t=si[e],r=null,n=Je.util.createBuffer(),i=new Array(80),a=0;a<80;++a)i[a]=new Array(2);var s=64;switch(e){case"SHA-384":s=48;break;case"SHA-512/256":s=32;break;case"SHA-512/224":s=28;break}var o={algorithm:e.replace("-","").toLowerCase(),blockLength:128,digestLength:s,messageLength:0,fullMessageLength:null,messageLengthSize:16};return o.start=function(){o.messageLength=0,o.fullMessageLength=o.messageLength128=[];for(var l=o.messageLengthSize/4,u=0;u>>0,c>>>0];for(var f=o.fullMessageLength.length-1;f>=0;--f)o.fullMessageLength[f]+=c[1],c[1]=c[0]+(o.fullMessageLength[f]/4294967296>>>0),o.fullMessageLength[f]=o.fullMessageLength[f]>>>0,c[0]=c[1]/4294967296>>>0;return n.putBytes(l),xh(r,i,n),(n.read>2048||n.length()===0)&&n.compact(),o},o.digest=function(){var l=Je.util.createBuffer();l.putBytes(n.bytes());var u=o.fullMessageLength[o.fullMessageLength.length-1]+o.messageLengthSize,c=u&o.blockLength-1;l.putBytes(Vc.substr(0,o.blockLength-c));for(var f,d,v=o.fullMessageLength[0]*8,g=0;g>>0,v+=d,l.putInt32(v>>>0),v=f>>>0;l.putInt32(v);for(var C=new Array(r.length),g=0;g=128;){for(Ce=0;Ce<16;++Ce)t[Ce][0]=r.getInt32()>>>0,t[Ce][1]=r.getInt32()>>>0;for(;Ce<80;++Ce)W=t[Ce-2],Le=W[0],te=W[1],n=((Le>>>19|te<<13)^(te>>>29|Le<<3)^Le>>>6)>>>0,i=((Le<<13|te>>>19)^(te<<3|Le>>>29)^(Le<<26|te>>>6))>>>0,ne=t[Ce-15],Le=ne[0],te=ne[1],a=((Le>>>1|te<<31)^(Le>>>8|te<<24)^Le>>>7)>>>0,s=((Le<<31|te>>>1)^(Le<<24|te>>>8)^(Le<<25|te>>>7))>>>0,ae=t[Ce-7],ee=t[Ce-16],te=i+ae[1]+s+ee[1],t[Ce][0]=n+ae[0]+a+ee[0]+(te/4294967296>>>0)>>>0,t[Ce][1]=te>>>0;for(C=e[0][0],I=e[0][1],y=e[1][0],m=e[1][1],S=e[2][0],B=e[2][1],R=e[3][0],L=e[3][1],M=e[4][0],V=e[4][1],Q=e[5][0],G=e[5][1],ce=e[6][0],pe=e[6][1],De=e[7][0],Ue=e[7][1],Ce=0;Ce<80;++Ce)u=((M>>>14|V<<18)^(M>>>18|V<<14)^(V>>>9|M<<23))>>>0,c=((M<<18|V>>>14)^(M<<14|V>>>18)^(V<<23|M>>>9))>>>0,f=(ce^M&(Q^ce))>>>0,d=(pe^V&(G^pe))>>>0,o=((C>>>28|I<<4)^(I>>>2|C<<30)^(I>>>7|C<<25))>>>0,l=((C<<4|I>>>28)^(I<<30|C>>>2)^(I<<25|C>>>7))>>>0,v=(C&y|S&(C^y))>>>0,g=(I&m|B&(I^m))>>>0,te=Ue+c+d+jc[Ce][1]+t[Ce][1],n=De+u+f+jc[Ce][0]+t[Ce][0]+(te/4294967296>>>0)>>>0,i=te>>>0,te=l+g,a=o+v+(te/4294967296>>>0)>>>0,s=te>>>0,De=ce,Ue=pe,ce=Q,pe=G,Q=M,G=V,te=L+i,M=R+n+(te/4294967296>>>0)>>>0,V=te>>>0,R=S,L=B,S=y,B=m,y=C,m=I,te=i+s,C=n+a+(te/4294967296>>>0)>>>0,I=te>>>0;te=e[0][1]+I,e[0][0]=e[0][0]+C+(te/4294967296>>>0)>>>0,e[0][1]=te>>>0,te=e[1][1]+m,e[1][0]=e[1][0]+y+(te/4294967296>>>0)>>>0,e[1][1]=te>>>0,te=e[2][1]+B,e[2][0]=e[2][0]+S+(te/4294967296>>>0)>>>0,e[2][1]=te>>>0,te=e[3][1]+L,e[3][0]=e[3][0]+R+(te/4294967296>>>0)>>>0,e[3][1]=te>>>0,te=e[4][1]+V,e[4][0]=e[4][0]+M+(te/4294967296>>>0)>>>0,e[4][1]=te>>>0,te=e[5][1]+G,e[5][0]=e[5][0]+Q+(te/4294967296>>>0)>>>0,e[5][1]=te>>>0,te=e[6][1]+pe,e[6][0]=e[6][0]+ce+(te/4294967296>>>0)>>>0,e[6][1]=te>>>0,te=e[7][1]+Ue,e[7][0]=e[7][0]+De+(te/4294967296>>>0)>>>0,e[7][1]=te>>>0,ie-=128}}var d0={},Kx=me,xt=Kx.asn1;d0.privateKeyValidator={name:"PrivateKeyInfo",tagClass:xt.Class.UNIVERSAL,type:xt.Type.SEQUENCE,constructed:!0,value:[{name:"PrivateKeyInfo.version",tagClass:xt.Class.UNIVERSAL,type:xt.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:xt.Class.UNIVERSAL,type:xt.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:xt.Class.UNIVERSAL,type:xt.Type.OID,constructed:!1,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:xt.Class.UNIVERSAL,type:xt.Type.OCTETSTRING,constructed:!1,capture:"privateKey"}]};d0.publicKeyValidator={name:"SubjectPublicKeyInfo",tagClass:xt.Class.UNIVERSAL,type:xt.Type.SEQUENCE,constructed:!0,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:xt.Class.UNIVERSAL,type:xt.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:xt.Class.UNIVERSAL,type:xt.Type.OID,constructed:!1,capture:"publicKeyOid"}]},{tagClass:xt.Class.UNIVERSAL,type:xt.Type.BITSTRING,constructed:!1,composed:!0,captureBitStringValue:"ed25519PublicKey"}]};var Nt=me,W1=d0,zx=W1.publicKeyValidator,Hx=W1.privateKeyValidator;if(typeof qx>"u")var qx=Nt.jsbn.BigInteger;var Kc=Nt.util.ByteBuffer,er=typeof Buffer>"u"?Uint8Array:Buffer;Nt.pki=Nt.pki||{};Nt.pki.ed25519=Nt.ed25519=Nt.ed25519||{};var Se=Nt.ed25519;Se.constants={};Se.constants.PUBLIC_KEY_BYTE_LENGTH=32;Se.constants.PRIVATE_KEY_BYTE_LENGTH=64;Se.constants.SEED_BYTE_LENGTH=32;Se.constants.SIGN_BYTE_LENGTH=64;Se.constants.HASH_BYTE_LENGTH=64;Se.generateKeyPair=function(e){e=e||{};var t=e.seed;if(t===void 0)t=Nt.random.getBytesSync(Se.constants.SEED_BYTE_LENGTH);else if(typeof t=="string"){if(t.length!==Se.constants.SEED_BYTE_LENGTH)throw new TypeError('"seed" must be '+Se.constants.SEED_BYTE_LENGTH+" bytes in length.")}else if(!(t instanceof Uint8Array))throw new TypeError('"seed" must be a node.js Buffer, Uint8Array, or a binary string.');t=hn({message:t,encoding:"binary"});for(var r=new er(Se.constants.PUBLIC_KEY_BYTE_LENGTH),n=new er(Se.constants.PRIVATE_KEY_BYTE_LENGTH),i=0;i<32;++i)n[i]=t[i];return Wx(r,n),{publicKey:r,privateKey:n}};Se.privateKeyFromAsn1=function(e){var t={},r=[],n=Nt.asn1.validate(e,Hx,t,r);if(!n){var i=new Error("Invalid Key.");throw i.errors=r,i}var a=Nt.asn1.derToOid(t.privateKeyOid),s=Nt.oids.EdDSA25519;if(a!==s)throw new Error('Invalid OID "'+a+'"; OID must be "'+s+'".');var o=t.privateKey,l=hn({message:Nt.asn1.fromDer(o).value,encoding:"binary"});return{privateKeyBytes:l}};Se.publicKeyFromAsn1=function(e){var t={},r=[],n=Nt.asn1.validate(e,zx,t,r);if(!n){var i=new Error("Invalid Key.");throw i.errors=r,i}var a=Nt.asn1.derToOid(t.publicKeyOid),s=Nt.oids.EdDSA25519;if(a!==s)throw new Error('Invalid OID "'+a+'"; OID must be "'+s+'".');var o=t.ed25519PublicKey;if(o.length!==Se.constants.PUBLIC_KEY_BYTE_LENGTH)throw new Error("Key length is invalid.");return hn({message:o,encoding:"binary"})};Se.publicKeyFromPrivateKey=function(e){e=e||{};var t=hn({message:e.privateKey,encoding:"binary"});if(t.length!==Se.constants.PRIVATE_KEY_BYTE_LENGTH)throw new TypeError('"options.privateKey" must have a byte length of '+Se.constants.PRIVATE_KEY_BYTE_LENGTH);for(var r=new er(Se.constants.PUBLIC_KEY_BYTE_LENGTH),n=0;n=0};function hn(e){var t=e.message;if(t instanceof Uint8Array||t instanceof er)return t;var r=e.encoding;if(t===void 0)if(e.md)t=e.md.digest().getBytes(),r="binary";else throw new TypeError('"options.message" or "options.md" not specified.');if(typeof t=="string"&&!r)throw new TypeError('"options.encoding" must be "binary" or "utf8".');if(typeof t=="string"){if(typeof Buffer<"u")return Buffer.from(t,r);t=new Kc(t,r)}else if(!(t instanceof Kc))throw new TypeError('"options.message" must be a node.js Buffer, a Uint8Array, a forge ByteBuffer, or a string with "options.encoding" specifying its encoding.');for(var n=new er(t.length()),i=0;i=32;--n){for(r=0,i=n-32,a=n-12;i>8,t[i]-=r*256;t[i]+=r,t[n]=0}for(r=0,i=0;i<32;++i)t[i]+=r-(t[31]>>4)*Iu[i],r=t[i]>>8,t[i]&=255;for(i=0;i<32;++i)t[i]-=r*Iu[i];for(n=0;n<32;++n)t[n+1]+=t[n]>>8,e[n]=t[n]&255}function Hc(e){for(var t=new Float64Array(64),r=0;r<64;++r)t[r]=e[r],e[r]=0;Y1(e,t)}function qc(e,t){var r=ue(),n=ue(),i=ue(),a=ue(),s=ue(),o=ue(),l=ue(),u=ue(),c=ue();Qi(r,e[1],e[0]),Qi(c,t[1],t[0]),Ve(r,r,c),Li(n,e[0],e[1]),Li(c,t[0],t[1]),Ve(n,n,c),Ve(i,e[3],t[3]),Ve(i,i,Gx),Ve(a,e[2],t[2]),Li(a,a,a),Qi(s,n,r),Qi(o,a,i),Li(l,a,i),Li(u,n,r),Ve(e[0],s,o),Ve(e[1],u,l),Ve(e[2],l,o),Ve(e[3],s,u)}function Ih(e,t,r){for(var n=0;n<4;++n)ev(e[n],t[n],r)}function h0(e,t){var r=ue(),n=ue(),i=ue();rT(i,t[2]),Ve(r,t[0],i),Ve(n,t[1],i),rl(e,n),e[31]^=Z1(r)<<7}function rl(e,t){var r,n,i,a=ue(),s=ue();for(r=0;r<16;++r)s[r]=t[r];for(_u(s),_u(s),_u(s),n=0;n<2;++n){for(a[0]=s[0]-65517,r=1;r<15;++r)a[r]=s[r]-65535-(a[r-1]>>16&1),a[r-1]&=65535;a[15]=s[15]-32767-(a[14]>>16&1),i=a[15]>>16&1,a[14]&=65535,ev(s,a,1-i)}for(r=0;r<16;r++)e[2*r]=s[r]&255,e[2*r+1]=s[r]>>8}function Zx(e,t){var r=ue(),n=ue(),i=ue(),a=ue(),s=ue(),o=ue(),l=ue();return Un(e[2],tl),Jx(e[1],t),oi(i,e[1]),Ve(a,i,$x),Qi(i,i,e[2]),Li(a,e[2],a),oi(s,a),oi(o,s),Ve(l,o,s),Ve(r,l,i),Ve(r,r,a),eT(r,r),Ve(r,r,i),Ve(r,r,a),Ve(r,r,a),Ve(e[0],r,a),oi(n,e[0]),Ve(n,n,a),_h(n,i)&&Ve(e[0],e[0],Qx),oi(n,e[0]),Ve(n,n,a),_h(n,i)?-1:(Z1(e[0])===t[31]>>7&&Qi(e[0],zc,e[0]),Ve(e[3],e[0],e[1]),0)}function Jx(e,t){var r;for(r=0;r<16;++r)e[r]=t[2*r]+(t[2*r+1]<<8);e[15]&=32767}function eT(e,t){var r=ue(),n;for(n=0;n<16;++n)r[n]=t[n];for(n=250;n>=0;--n)oi(r,r),n!==1&&Ve(r,r,t);for(n=0;n<16;++n)e[n]=r[n]}function _h(e,t){var r=new er(32),n=new er(32);return rl(r,e),rl(n,t),X1(r,0,n,0)}function X1(e,t,r,n){return tT(e,t,r,n,32)}function tT(e,t,r,n,i){var a,s=0;for(a=0;a>>8)-1}function Z1(e){var t=new er(32);return rl(t,e),t[0]&1}function J1(e,t,r){var n,i;for(Un(e[0],zc),Un(e[1],tl),Un(e[2],tl),Un(e[3],zc),i=255;i>=0;--i)n=r[i/8|0]>>(i&7)&1,Ih(e,t,n),qc(t,e),qc(e,e),Ih(e,t,n)}function p0(e,t){var r=[ue(),ue(),ue(),ue()];Un(r[0],Th),Un(r[1],wh),Un(r[2],tl),Ve(r[3],Th,wh),J1(e,r,t)}function Un(e,t){var r;for(r=0;r<16;r++)e[r]=t[r]|0}function rT(e,t){var r=ue(),n;for(n=0;n<16;++n)r[n]=t[n];for(n=253;n>=0;--n)oi(r,r),n!==2&&n!==4&&Ve(r,r,t);for(n=0;n<16;++n)e[n]=r[n]}function _u(e){var t,r,n=1;for(t=0;t<16;++t)r=e[t]+n+65535,n=Math.floor(r/65536),e[t]=r-n*65536;e[0]+=n-1+37*(n-1)}function ev(e,t,r){for(var n,i=~(r-1),a=0;a<16;++a)n=i&(e[a]^t[a]),e[a]^=n,t[a]^=n}function ue(e){var t,r=new Float64Array(16);if(e)for(t=0;t0&&(o=or.util.fillString(String.fromCharCode(0),l)+o);var u=i.encrypt(o,"NONE"),c=e.generate(o,a);return{encapsulation:u,key:c}},n.decrypt=function(i,a,s){var o=i.decrypt(a,"NONE");return e.generate(o,s)},n};or.kem.kdf1=function(e,t){tv(this,e,0,t||e.digestLength)};or.kem.kdf2=function(e,t){tv(this,e,1,t||e.digestLength)};function tv(e,t,r,n){e.generate=function(i,a){for(var s=new or.util.ByteBuffer,o=Math.ceil(a/n)+r,l=new or.util.ByteBuffer,u=r;u"u"||t?e.flags|=Te.log.LEVEL_LOCKED:e.flags&=~Te.log.LEVEL_LOCKED};Te.log.addLogger=function(e){$c.push(e)};if(typeof console<"u"&&"log"in console){var La;if(console.error&&console.warn&&console.info&&console.debug){var nT={error:console.error,warning:console.warn,info:console.info,debug:console.debug,verbose:console.debug},g0=function(e,t){Te.log.prepareStandard(t);var r=nT[t.level],n=[t.standard];n=n.concat(t.arguments.slice()),r.apply(console,n)};La=Te.log.makeLogger(g0)}else{var g0=function(t,r){Te.log.prepareStandardFull(r),console.log(r.standardFull)};La=Te.log.makeLogger(g0)}Te.log.setLevel(La,"debug"),Te.log.addLogger(La),ts=La}else console={log:function(){}};if(ts!==null&&typeof window<"u"&&window.location){var ro=new URL(window.location.href).searchParams;if(ro.has("console.level")&&Te.log.setLevel(ts,ro.get("console.level").slice(-1)[0]),ro.has("console.lock")){var iT=ro.get("console.lock").slice(-1)[0];iT=="true"&&Te.log.lock(ts)}}Te.log.consoleLogger=ts;var Y=me,k=Y.asn1,zt=Y.pkcs7=Y.pkcs7||{};zt.messageFromPem=function(e){var t=Y.pem.decode(e)[0];if(t.type!=="PKCS7"){var r=new Error('Could not convert PKCS#7 message from PEM; PEM header type is not "PKCS#7".');throw r.headerType=t.type,r}if(t.procType&&t.procType.type==="ENCRYPTED")throw new Error("Could not convert PKCS#7 message from PEM; PEM is encrypted.");var n=k.fromDer(t.body);return zt.messageFromAsn1(n)};zt.messageToPem=function(e,t){var r={type:"PKCS7",body:k.toDer(e.toAsn1()).getBytes()};return Y.pem.encode(r,{maxline:t})};zt.messageFromAsn1=function(e){var t={},r=[];if(!k.validate(e,zt.asn1.contentInfoValidator,t,r)){var n=new Error("Cannot read PKCS#7 message. ASN.1 object is not an PKCS#7 ContentInfo.");throw n.errors=r,n}var i=k.derToOid(t.contentType),a;switch(i){case Y.pki.oids.envelopedData:a=zt.createEnvelopedData();break;case Y.pki.oids.encryptedData:a=zt.createEncryptedData();break;case Y.pki.oids.signedData:a=zt.createSignedData();break;default:throw new Error("Cannot read PKCS#7 message. ContentType with OID "+i+" is not (yet) supported.")}return a.fromAsn1(t.content.value[0]),a};zt.createSignedData=function(){var e=null;return e={type:Y.pki.oids.signedData,version:1,certificates:[],crls:[],signers:[],digestAlgorithmIdentifiers:[],contentInfo:null,signerInfos:[],fromAsn1:function(n){if(v0(e,n,zt.asn1.signedDataValidator),e.certificates=[],e.crls=[],e.digestAlgorithmIdentifiers=[],e.contentInfo=null,e.signerInfos=[],e.rawCapture.certificates)for(var i=e.rawCapture.certificates.value,a=0;a0&&s.value[0].value.push(k.create(k.Class.CONTEXT_SPECIFIC,0,!0,n)),a.length>0&&s.value[0].value.push(k.create(k.Class.CONTEXT_SPECIFIC,1,!0,a)),s.value[0].value.push(k.create(k.Class.UNIVERSAL,k.Type.SET,!0,e.signerInfos)),k.create(k.Class.UNIVERSAL,k.Type.SEQUENCE,!0,[k.create(k.Class.UNIVERSAL,k.Type.OID,!1,k.oidToDer(e.type).getBytes()),s])},addSigner:function(n){var i=n.issuer,a=n.serialNumber;if(n.certificate){var s=n.certificate;typeof s=="string"&&(s=Y.pki.certificateFromPem(s)),i=s.issuer.attributes,a=s.serialNumber}var o=n.key;if(!o)throw new Error("Could not add PKCS#7 signer; no private key specified.");typeof o=="string"&&(o=Y.pki.privateKeyFromPem(o));var l=n.digestAlgorithm||Y.pki.oids.sha1;switch(l){case Y.pki.oids.sha1:case Y.pki.oids.sha256:case Y.pki.oids.sha384:case Y.pki.oids.sha512:case Y.pki.oids.md5:break;default:throw new Error("Could not add PKCS#7 signer; unknown message digest algorithm: "+l)}var u=n.authenticatedAttributes||[];if(u.length>0){for(var c=!1,f=!1,d=0;d0){for(var r=k.create(k.Class.CONTEXT_SPECIFIC,1,!0,[]),n=0;n=r&&i="8"&&(r="00"+r);var n=pt.util.hexToBytes(r);e.putInt32(n.length),e.putBytes(n)}function Wi(e,t){e.putInt32(t.length),e.putString(t)}function no(){for(var e=pt.md.sha1.create(),t=arguments.length,r=0;r0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");r===-1&&(r=t);var n=r===t?0:4-r%4;return[r,n]}function gT(e){var t=nv(e),r=t[0],n=t[1];return(r+n)*3/4-n}function vT(e,t,r){return(t+r)*3/4-r}function yT(e){var t,r=nv(e),n=r[0],i=r[1],a=new hT(vT(e,n,i)),s=0,o=i>0?n-4:n,l;for(l=0;l>16&255,a[s++]=t>>8&255,a[s++]=t&255;return i===2&&(t=Cr[e.charCodeAt(l)]<<2|Cr[e.charCodeAt(l+1)]>>4,a[s++]=t&255),i===1&&(t=Cr[e.charCodeAt(l)]<<10|Cr[e.charCodeAt(l+1)]<<4|Cr[e.charCodeAt(l+2)]>>2,a[s++]=t>>8&255,a[s++]=t&255),a}function mT(e){return Zr[e>>18&63]+Zr[e>>12&63]+Zr[e>>6&63]+Zr[e&63]}function CT(e,t,r){for(var n,i=[],a=t;ao?o:s+a));return n===1?(t=e[r-1],i.push(Zr[t>>2]+Zr[t<<4&63]+"==")):n===2&&(t=(e[r-2]<<8)+e[r-1],i.push(Zr[t>>10]+Zr[t>>4&63]+Zr[t<<2&63]+"=")),i.join("")}var y0={};/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */y0.read=function(e,t,r,n,i){var a,s,o=i*8-n-1,l=(1<>1,c=-7,f=r?i-1:0,d=r?-1:1,v=e[t+f];for(f+=d,a=v&(1<<-c)-1,v>>=-c,c+=o;c>0;a=a*256+e[t+f],f+=d,c-=8);for(s=a&(1<<-c)-1,a>>=-c,c+=n;c>0;s=s*256+e[t+f],f+=d,c-=8);if(a===0)a=1-u;else{if(a===l)return s?NaN:(v?-1:1)*(1/0);s=s+Math.pow(2,n),a=a-u}return(v?-1:1)*s*Math.pow(2,a-n)};y0.write=function(e,t,r,n,i,a){var s,o,l,u=a*8-i-1,c=(1<>1,d=i===23?Math.pow(2,-24)-Math.pow(2,-77):0,v=n?0:a-1,g=n?1:-1,C=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(o=isNaN(t)?1:0,s=c):(s=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-s))<1&&(s--,l*=2),s+f>=1?t+=d/l:t+=d*Math.pow(2,1-f),t*l>=2&&(s++,l/=2),s+f>=c?(o=0,s=c):s+f>=1?(o=(t*l-1)*Math.pow(2,i),s=s+f):(o=t*Math.pow(2,f-1)*Math.pow(2,i),s=0));i>=8;e[r+v]=o&255,v+=g,o/=256,i-=8);for(s=s<0;e[r+v]=s&255,v+=g,s/=256,u-=8);e[r+v-g]|=C*128};/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */(function(e){const t=Dl,r=y0,n=typeof Symbol=="function"&&typeof Symbol.for=="function"?Symbol.for("nodejs.util.inspect.custom"):null;e.Buffer=o,e.SlowBuffer=m,e.INSPECT_MAX_BYTES=50;const i=2147483647;e.kMaxLength=i,o.TYPED_ARRAY_SUPPORT=a(),!o.TYPED_ARRAY_SUPPORT&&typeof console<"u"&&typeof console.error=="function"&&console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support.");function a(){try{const w=new Uint8Array(1),h={foo:function(){return 42}};return Object.setPrototypeOf(h,Uint8Array.prototype),Object.setPrototypeOf(w,h),w.foo()===42}catch{return!1}}Object.defineProperty(o.prototype,"parent",{enumerable:!0,get:function(){if(o.isBuffer(this))return this.buffer}}),Object.defineProperty(o.prototype,"offset",{enumerable:!0,get:function(){if(o.isBuffer(this))return this.byteOffset}});function s(w){if(w>i)throw new RangeError('The value "'+w+'" is invalid for option "size"');const h=new Uint8Array(w);return Object.setPrototypeOf(h,o.prototype),h}function o(w,h,p){if(typeof w=="number"){if(typeof h=="string")throw new TypeError('The "string" argument must be of type string. Received type number');return f(w)}return l(w,h,p)}o.poolSize=8192;function l(w,h,p){if(typeof w=="string")return d(w,h);if(ArrayBuffer.isView(w))return g(w);if(w==null)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof w);if(Gt(w,ArrayBuffer)||w&&Gt(w.buffer,ArrayBuffer)||typeof SharedArrayBuffer<"u"&&(Gt(w,SharedArrayBuffer)||w&&Gt(w.buffer,SharedArrayBuffer)))return C(w,h,p);if(typeof w=="number")throw new TypeError('The "value" argument must not be of type number. Received type number');const _=w.valueOf&&w.valueOf();if(_!=null&&_!==w)return o.from(_,h,p);const N=I(w);if(N)return N;if(typeof Symbol<"u"&&Symbol.toPrimitive!=null&&typeof w[Symbol.toPrimitive]=="function")return o.from(w[Symbol.toPrimitive]("string"),h,p);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof w)}o.from=function(w,h,p){return l(w,h,p)},Object.setPrototypeOf(o.prototype,Uint8Array.prototype),Object.setPrototypeOf(o,Uint8Array);function u(w){if(typeof w!="number")throw new TypeError('"size" argument must be of type number');if(w<0)throw new RangeError('The value "'+w+'" is invalid for option "size"')}function c(w,h,p){return u(w),w<=0?s(w):h!==void 0?typeof p=="string"?s(w).fill(h,p):s(w).fill(h):s(w)}o.alloc=function(w,h,p){return c(w,h,p)};function f(w){return u(w),s(w<0?0:y(w)|0)}o.allocUnsafe=function(w){return f(w)},o.allocUnsafeSlow=function(w){return f(w)};function d(w,h){if((typeof h!="string"||h==="")&&(h="utf8"),!o.isEncoding(h))throw new TypeError("Unknown encoding: "+h);const p=S(w,h)|0;let _=s(p);const N=_.write(w,h);return N!==p&&(_=_.slice(0,N)),_}function v(w){const h=w.length<0?0:y(w.length)|0,p=s(h);for(let _=0;_=i)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+i.toString(16)+" bytes");return w|0}function m(w){return+w!=w&&(w=0),o.alloc(+w)}o.isBuffer=function(h){return h!=null&&h._isBuffer===!0&&h!==o.prototype},o.compare=function(h,p){if(Gt(h,Uint8Array)&&(h=o.from(h,h.offset,h.byteLength)),Gt(p,Uint8Array)&&(p=o.from(p,p.offset,p.byteLength)),!o.isBuffer(h)||!o.isBuffer(p))throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array');if(h===p)return 0;let _=h.length,N=p.length;for(let P=0,F=Math.min(_,N);PN.length?(o.isBuffer(F)||(F=o.from(F)),F.copy(N,P)):Uint8Array.prototype.set.call(N,F,P);else if(o.isBuffer(F))F.copy(N,P);else throw new TypeError('"list" argument must be an Array of Buffers');P+=F.length}return N};function S(w,h){if(o.isBuffer(w))return w.length;if(ArrayBuffer.isView(w)||Gt(w,ArrayBuffer))return w.byteLength;if(typeof w!="string")throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof w);const p=w.length,_=arguments.length>2&&arguments[2]===!0;if(!_&&p===0)return 0;let N=!1;for(;;)switch(h){case"ascii":case"latin1":case"binary":return p;case"utf8":case"utf-8":return Ea(w).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return p*2;case"hex":return p>>>1;case"base64":return Ai(w).length;default:if(N)return _?-1:Ea(w).length;h=(""+h).toLowerCase(),N=!0}}o.byteLength=S;function B(w,h,p){let _=!1;if((h===void 0||h<0)&&(h=0),h>this.length||((p===void 0||p>this.length)&&(p=this.length),p<=0)||(p>>>=0,h>>>=0,p<=h))return"";for(w||(w="utf8");;)switch(w){case"hex":return ae(this,h,p);case"utf8":case"utf-8":return Ue(this,h,p);case"ascii":return te(this,h,p);case"latin1":case"binary":return W(this,h,p);case"base64":return De(this,h,p);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return ne(this,h,p);default:if(_)throw new TypeError("Unknown encoding: "+w);w=(w+"").toLowerCase(),_=!0}}o.prototype._isBuffer=!0;function R(w,h,p){const _=w[h];w[h]=w[p],w[p]=_}o.prototype.swap16=function(){const h=this.length;if(h%2!==0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let p=0;pp&&(h+=" ... "),""},n&&(o.prototype[n]=o.prototype.inspect),o.prototype.compare=function(h,p,_,N,P){if(Gt(h,Uint8Array)&&(h=o.from(h,h.offset,h.byteLength)),!o.isBuffer(h))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof h);if(p===void 0&&(p=0),_===void 0&&(_=h?h.length:0),N===void 0&&(N=0),P===void 0&&(P=this.length),p<0||_>h.length||N<0||P>this.length)throw new RangeError("out of range index");if(N>=P&&p>=_)return 0;if(N>=P)return-1;if(p>=_)return 1;if(p>>>=0,_>>>=0,N>>>=0,P>>>=0,this===h)return 0;let F=P-N,ge=_-p;const tt=Math.min(F,ge),ze=this.slice(N,P),He=h.slice(p,_);for(let Ne=0;Ne2147483647?p=2147483647:p<-2147483648&&(p=-2147483648),p=+p,En(p)&&(p=N?0:w.length-1),p<0&&(p=w.length+p),p>=w.length){if(N)return-1;p=w.length-1}else if(p<0)if(N)p=0;else return-1;if(typeof h=="string"&&(h=o.from(h,_)),o.isBuffer(h))return h.length===0?-1:M(w,h,p,_,N);if(typeof h=="number")return h=h&255,typeof Uint8Array.prototype.indexOf=="function"?N?Uint8Array.prototype.indexOf.call(w,h,p):Uint8Array.prototype.lastIndexOf.call(w,h,p):M(w,[h],p,_,N);throw new TypeError("val must be string, number or Buffer")}function M(w,h,p,_,N){let P=1,F=w.length,ge=h.length;if(_!==void 0&&(_=String(_).toLowerCase(),_==="ucs2"||_==="ucs-2"||_==="utf16le"||_==="utf-16le")){if(w.length<2||h.length<2)return-1;P=2,F/=2,ge/=2,p/=2}function tt(He,Ne){return P===1?He[Ne]:He.readUInt16BE(Ne*P)}let ze;if(N){let He=-1;for(ze=p;zeF&&(p=F-ge),ze=p;ze>=0;ze--){let He=!0;for(let Ne=0;NeN&&(_=N)):_=N;const P=h.length;_>P/2&&(_=P/2);let F;for(F=0;F<_;++F){const ge=parseInt(h.substr(F*2,2),16);if(En(ge))return F;w[p+F]=ge}return F}function Q(w,h,p,_){return tr(Ea(h,w.length-p),w,p,_)}function G(w,h,p,_){return tr(Sa(h),w,p,_)}function ce(w,h,p,_){return tr(Ai(h),w,p,_)}function pe(w,h,p,_){return tr(Ds(h,w.length-p),w,p,_)}o.prototype.write=function(h,p,_,N){if(p===void 0)N="utf8",_=this.length,p=0;else if(_===void 0&&typeof p=="string")N=p,_=this.length,p=0;else if(isFinite(p))p=p>>>0,isFinite(_)?(_=_>>>0,N===void 0&&(N="utf8")):(N=_,_=void 0);else throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");const P=this.length-p;if((_===void 0||_>P)&&(_=P),h.length>0&&(_<0||p<0)||p>this.length)throw new RangeError("Attempt to write outside buffer bounds");N||(N="utf8");let F=!1;for(;;)switch(N){case"hex":return V(this,h,p,_);case"utf8":case"utf-8":return Q(this,h,p,_);case"ascii":case"latin1":case"binary":return G(this,h,p,_);case"base64":return ce(this,h,p,_);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return pe(this,h,p,_);default:if(F)throw new TypeError("Unknown encoding: "+N);N=(""+N).toLowerCase(),F=!0}},o.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function De(w,h,p){return h===0&&p===w.length?t.fromByteArray(w):t.fromByteArray(w.slice(h,p))}function Ue(w,h,p){p=Math.min(w.length,p);const _=[];let N=h;for(;N239?4:P>223?3:P>191?2:1;if(N+ge<=p){let tt,ze,He,Ne;switch(ge){case 1:P<128&&(F=P);break;case 2:tt=w[N+1],(tt&192)===128&&(Ne=(P&31)<<6|tt&63,Ne>127&&(F=Ne));break;case 3:tt=w[N+1],ze=w[N+2],(tt&192)===128&&(ze&192)===128&&(Ne=(P&15)<<12|(tt&63)<<6|ze&63,Ne>2047&&(Ne<55296||Ne>57343)&&(F=Ne));break;case 4:tt=w[N+1],ze=w[N+2],He=w[N+3],(tt&192)===128&&(ze&192)===128&&(He&192)===128&&(Ne=(P&15)<<18|(tt&63)<<12|(ze&63)<<6|He&63,Ne>65535&&Ne<1114112&&(F=Ne))}}F===null?(F=65533,ge=1):F>65535&&(F-=65536,_.push(F>>>10&1023|55296),F=56320|F&1023),_.push(F),N+=ge}return Le(_)}const Ce=4096;function Le(w){const h=w.length;if(h<=Ce)return String.fromCharCode.apply(String,w);let p="",_=0;for(;__)&&(p=_);let N="";for(let P=h;P_&&(h=_),p<0?(p+=_,p<0&&(p=0)):p>_&&(p=_),pp)throw new RangeError("Trying to access beyond buffer length")}o.prototype.readUintLE=o.prototype.readUIntLE=function(h,p,_){h=h>>>0,p=p>>>0,_||ee(h,p,this.length);let N=this[h],P=1,F=0;for(;++F>>0,p=p>>>0,_||ee(h,p,this.length);let N=this[h+--p],P=1;for(;p>0&&(P*=256);)N+=this[h+--p]*P;return N},o.prototype.readUint8=o.prototype.readUInt8=function(h,p){return h=h>>>0,p||ee(h,1,this.length),this[h]},o.prototype.readUint16LE=o.prototype.readUInt16LE=function(h,p){return h=h>>>0,p||ee(h,2,this.length),this[h]|this[h+1]<<8},o.prototype.readUint16BE=o.prototype.readUInt16BE=function(h,p){return h=h>>>0,p||ee(h,2,this.length),this[h]<<8|this[h+1]},o.prototype.readUint32LE=o.prototype.readUInt32LE=function(h,p){return h=h>>>0,p||ee(h,4,this.length),(this[h]|this[h+1]<<8|this[h+2]<<16)+this[h+3]*16777216},o.prototype.readUint32BE=o.prototype.readUInt32BE=function(h,p){return h=h>>>0,p||ee(h,4,this.length),this[h]*16777216+(this[h+1]<<16|this[h+2]<<8|this[h+3])},o.prototype.readBigUInt64LE=Kr(function(h){h=h>>>0,We(h,"offset");const p=this[h],_=this[h+7];(p===void 0||_===void 0)&&Ke(h,this.length-8);const N=p+this[++h]*2**8+this[++h]*2**16+this[++h]*2**24,P=this[++h]+this[++h]*2**8+this[++h]*2**16+_*2**24;return BigInt(N)+(BigInt(P)<>>0,We(h,"offset");const p=this[h],_=this[h+7];(p===void 0||_===void 0)&&Ke(h,this.length-8);const N=p*2**24+this[++h]*2**16+this[++h]*2**8+this[++h],P=this[++h]*2**24+this[++h]*2**16+this[++h]*2**8+_;return(BigInt(N)<>>0,p=p>>>0,_||ee(h,p,this.length);let N=this[h],P=1,F=0;for(;++F=P&&(N-=Math.pow(2,8*p)),N},o.prototype.readIntBE=function(h,p,_){h=h>>>0,p=p>>>0,_||ee(h,p,this.length);let N=p,P=1,F=this[h+--N];for(;N>0&&(P*=256);)F+=this[h+--N]*P;return P*=128,F>=P&&(F-=Math.pow(2,8*p)),F},o.prototype.readInt8=function(h,p){return h=h>>>0,p||ee(h,1,this.length),this[h]&128?(255-this[h]+1)*-1:this[h]},o.prototype.readInt16LE=function(h,p){h=h>>>0,p||ee(h,2,this.length);const _=this[h]|this[h+1]<<8;return _&32768?_|4294901760:_},o.prototype.readInt16BE=function(h,p){h=h>>>0,p||ee(h,2,this.length);const _=this[h+1]|this[h]<<8;return _&32768?_|4294901760:_},o.prototype.readInt32LE=function(h,p){return h=h>>>0,p||ee(h,4,this.length),this[h]|this[h+1]<<8|this[h+2]<<16|this[h+3]<<24},o.prototype.readInt32BE=function(h,p){return h=h>>>0,p||ee(h,4,this.length),this[h]<<24|this[h+1]<<16|this[h+2]<<8|this[h+3]},o.prototype.readBigInt64LE=Kr(function(h){h=h>>>0,We(h,"offset");const p=this[h],_=this[h+7];(p===void 0||_===void 0)&&Ke(h,this.length-8);const N=this[h+4]+this[h+5]*2**8+this[h+6]*2**16+(_<<24);return(BigInt(N)<>>0,We(h,"offset");const p=this[h],_=this[h+7];(p===void 0||_===void 0)&&Ke(h,this.length-8);const N=(p<<24)+this[++h]*2**16+this[++h]*2**8+this[++h];return(BigInt(N)<>>0,p||ee(h,4,this.length),r.read(this,h,!0,23,4)},o.prototype.readFloatBE=function(h,p){return h=h>>>0,p||ee(h,4,this.length),r.read(this,h,!1,23,4)},o.prototype.readDoubleLE=function(h,p){return h=h>>>0,p||ee(h,8,this.length),r.read(this,h,!0,52,8)},o.prototype.readDoubleBE=function(h,p){return h=h>>>0,p||ee(h,8,this.length),r.read(this,h,!1,52,8)};function ie(w,h,p,_,N,P){if(!o.isBuffer(w))throw new TypeError('"buffer" argument must be a Buffer instance');if(h>N||hw.length)throw new RangeError("Index out of range")}o.prototype.writeUintLE=o.prototype.writeUIntLE=function(h,p,_,N){if(h=+h,p=p>>>0,_=_>>>0,!N){const ge=Math.pow(2,8*_)-1;ie(this,h,p,_,ge,0)}let P=1,F=0;for(this[p]=h&255;++F<_&&(P*=256);)this[p+F]=h/P&255;return p+_},o.prototype.writeUintBE=o.prototype.writeUIntBE=function(h,p,_,N){if(h=+h,p=p>>>0,_=_>>>0,!N){const ge=Math.pow(2,8*_)-1;ie(this,h,p,_,ge,0)}let P=_-1,F=1;for(this[p+P]=h&255;--P>=0&&(F*=256);)this[p+P]=h/F&255;return p+_},o.prototype.writeUint8=o.prototype.writeUInt8=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,1,255,0),this[p]=h&255,p+1},o.prototype.writeUint16LE=o.prototype.writeUInt16LE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,2,65535,0),this[p]=h&255,this[p+1]=h>>>8,p+2},o.prototype.writeUint16BE=o.prototype.writeUInt16BE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,2,65535,0),this[p]=h>>>8,this[p+1]=h&255,p+2},o.prototype.writeUint32LE=o.prototype.writeUInt32LE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,4,4294967295,0),this[p+3]=h>>>24,this[p+2]=h>>>16,this[p+1]=h>>>8,this[p]=h&255,p+4},o.prototype.writeUint32BE=o.prototype.writeUInt32BE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,4,4294967295,0),this[p]=h>>>24,this[p+1]=h>>>16,this[p+2]=h>>>8,this[p+3]=h&255,p+4};function Oe(w,h,p,_,N){je(h,_,N,w,p,7);let P=Number(h&BigInt(4294967295));w[p++]=P,P=P>>8,w[p++]=P,P=P>>8,w[p++]=P,P=P>>8,w[p++]=P;let F=Number(h>>BigInt(32)&BigInt(4294967295));return w[p++]=F,F=F>>8,w[p++]=F,F=F>>8,w[p++]=F,F=F>>8,w[p++]=F,p}function se(w,h,p,_,N){je(h,_,N,w,p,7);let P=Number(h&BigInt(4294967295));w[p+7]=P,P=P>>8,w[p+6]=P,P=P>>8,w[p+5]=P,P=P>>8,w[p+4]=P;let F=Number(h>>BigInt(32)&BigInt(4294967295));return w[p+3]=F,F=F>>8,w[p+2]=F,F=F>>8,w[p+1]=F,F=F>>8,w[p]=F,p+8}o.prototype.writeBigUInt64LE=Kr(function(h,p=0){return Oe(this,h,p,BigInt(0),BigInt("0xffffffffffffffff"))}),o.prototype.writeBigUInt64BE=Kr(function(h,p=0){return se(this,h,p,BigInt(0),BigInt("0xffffffffffffffff"))}),o.prototype.writeIntLE=function(h,p,_,N){if(h=+h,p=p>>>0,!N){const tt=Math.pow(2,8*_-1);ie(this,h,p,_,tt-1,-tt)}let P=0,F=1,ge=0;for(this[p]=h&255;++P<_&&(F*=256);)h<0&&ge===0&&this[p+P-1]!==0&&(ge=1),this[p+P]=(h/F>>0)-ge&255;return p+_},o.prototype.writeIntBE=function(h,p,_,N){if(h=+h,p=p>>>0,!N){const tt=Math.pow(2,8*_-1);ie(this,h,p,_,tt-1,-tt)}let P=_-1,F=1,ge=0;for(this[p+P]=h&255;--P>=0&&(F*=256);)h<0&&ge===0&&this[p+P+1]!==0&&(ge=1),this[p+P]=(h/F>>0)-ge&255;return p+_},o.prototype.writeInt8=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,1,127,-128),h<0&&(h=255+h+1),this[p]=h&255,p+1},o.prototype.writeInt16LE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,2,32767,-32768),this[p]=h&255,this[p+1]=h>>>8,p+2},o.prototype.writeInt16BE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,2,32767,-32768),this[p]=h>>>8,this[p+1]=h&255,p+2},o.prototype.writeInt32LE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,4,2147483647,-2147483648),this[p]=h&255,this[p+1]=h>>>8,this[p+2]=h>>>16,this[p+3]=h>>>24,p+4},o.prototype.writeInt32BE=function(h,p,_){return h=+h,p=p>>>0,_||ie(this,h,p,4,2147483647,-2147483648),h<0&&(h=4294967295+h+1),this[p]=h>>>24,this[p+1]=h>>>16,this[p+2]=h>>>8,this[p+3]=h&255,p+4},o.prototype.writeBigInt64LE=Kr(function(h,p=0){return Oe(this,h,p,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))}),o.prototype.writeBigInt64BE=Kr(function(h,p=0){return se(this,h,p,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))});function Fe(w,h,p,_,N,P){if(p+_>w.length)throw new RangeError("Index out of range");if(p<0)throw new RangeError("Index out of range")}function Re(w,h,p,_,N){return h=+h,p=p>>>0,N||Fe(w,h,p,4),r.write(w,h,p,_,23,4),p+4}o.prototype.writeFloatLE=function(h,p,_){return Re(this,h,p,!0,_)},o.prototype.writeFloatBE=function(h,p,_){return Re(this,h,p,!1,_)};function ke(w,h,p,_,N){return h=+h,p=p>>>0,N||Fe(w,h,p,8),r.write(w,h,p,_,52,8),p+8}o.prototype.writeDoubleLE=function(h,p,_){return ke(this,h,p,!0,_)},o.prototype.writeDoubleBE=function(h,p,_){return ke(this,h,p,!1,_)},o.prototype.copy=function(h,p,_,N){if(!o.isBuffer(h))throw new TypeError("argument should be a Buffer");if(_||(_=0),!N&&N!==0&&(N=this.length),p>=h.length&&(p=h.length),p||(p=0),N>0&&N<_&&(N=_),N===_||h.length===0||this.length===0)return 0;if(p<0)throw new RangeError("targetStart out of bounds");if(_<0||_>=this.length)throw new RangeError("Index out of range");if(N<0)throw new RangeError("sourceEnd out of bounds");N>this.length&&(N=this.length),h.length-p>>0,_=_===void 0?this.length:_>>>0,h||(h=0);let P;if(typeof h=="number")for(P=p;P<_;++P)this[P]=h;else{const F=o.isBuffer(h)?h:o.from(h,N),ge=F.length;if(ge===0)throw new TypeError('The value "'+h+'" is invalid for argument "value"');for(P=0;P<_-p;++P)this[P+p]=F[P%ge]}return this};const be={};function Qe(w,h,p){be[w]=class extends p{constructor(){super(),Object.defineProperty(this,"message",{value:h.apply(this,arguments),writable:!0,configurable:!0}),this.name=`${this.name} [${w}]`,this.stack,delete this.name}get code(){return w}set code(N){Object.defineProperty(this,"code",{configurable:!0,enumerable:!0,value:N,writable:!0})}toString(){return`${this.name} [${w}]: ${this.message}`}}}Qe("ERR_BUFFER_OUT_OF_BOUNDS",function(w){return w?`${w} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"},RangeError),Qe("ERR_INVALID_ARG_TYPE",function(w,h){return`The "${w}" argument must be of type number. Received type ${typeof h}`},TypeError),Qe("ERR_OUT_OF_RANGE",function(w,h,p){let _=`The value of "${w}" is out of range.`,N=p;return Number.isInteger(p)&&Math.abs(p)>2**32?N=ct(String(p)):typeof p=="bigint"&&(N=String(p),(p>BigInt(2)**BigInt(32)||p<-(BigInt(2)**BigInt(32)))&&(N=ct(N)),N+="n"),_+=` It must be ${h}. Received ${N}`,_},RangeError);function ct(w){let h="",p=w.length;const _=w[0]==="-"?1:0;for(;p>=_+4;p-=3)h=`_${w.slice(p-3,p)}${h}`;return`${w.slice(0,p)}${h}`}function ot(w,h,p){We(h,"offset"),(w[h]===void 0||w[h+p]===void 0)&&Ke(h,w.length-(p+1))}function je(w,h,p,_,N,P){if(w>p||w3?h===0||h===BigInt(0)?ge=`>= 0${F} and < 2${F} ** ${(P+1)*8}${F}`:ge=`>= -(2${F} ** ${(P+1)*8-1}${F}) and < 2 ** ${(P+1)*8-1}${F}`:ge=`>= ${h}${F} and <= ${p}${F}`,new be.ERR_OUT_OF_RANGE("value",ge,w)}ot(_,N,P)}function We(w,h){if(typeof w!="number")throw new be.ERR_INVALID_ARG_TYPE(h,"number",w)}function Ke(w,h,p){throw Math.floor(w)!==w?(We(w,p),new be.ERR_OUT_OF_RANGE(p||"offset","an integer",w)):h<0?new be.ERR_BUFFER_OUT_OF_BOUNDS:new be.ERR_OUT_OF_RANGE(p||"offset",`>= ${p?1:0} and <= ${h}`,w)}const ht=/[^+/0-9A-Za-z-_]/g;function Ns(w){if(w=w.split("=")[0],w=w.trim().replace(ht,""),w.length<2)return"";for(;w.length%4!==0;)w=w+"=";return w}function Ea(w,h){h=h||1/0;let p;const _=w.length;let N=null;const P=[];for(let F=0;F<_;++F){if(p=w.charCodeAt(F),p>55295&&p<57344){if(!N){if(p>56319){(h-=3)>-1&&P.push(239,191,189);continue}else if(F+1===_){(h-=3)>-1&&P.push(239,191,189);continue}N=p;continue}if(p<56320){(h-=3)>-1&&P.push(239,191,189),N=p;continue}p=(N-55296<<10|p-56320)+65536}else N&&(h-=3)>-1&&P.push(239,191,189);if(N=null,p<128){if((h-=1)<0)break;P.push(p)}else if(p<2048){if((h-=2)<0)break;P.push(p>>6|192,p&63|128)}else if(p<65536){if((h-=3)<0)break;P.push(p>>12|224,p>>6&63|128,p&63|128)}else if(p<1114112){if((h-=4)<0)break;P.push(p>>18|240,p>>12&63|128,p>>6&63|128,p&63|128)}else throw new Error("Invalid code point")}return P}function Sa(w){const h=[];for(let p=0;p>8,N=p%256,P.push(N),P.push(_);return P}function Ai(w){return t.toByteArray(Ns(w))}function tr(w,h,p,_){let N;for(N=0;N<_&&!(N+p>=h.length||N>=w.length);++N)h[N+p]=w[N];return N}function Gt(w,h){return w instanceof h||w!=null&&w.constructor!=null&&w.constructor.name!=null&&w.constructor.name===h.name}function En(w){return w!==w}const Ll=function(){const w="0123456789abcdef",h=new Array(256);for(let p=0;p<16;++p){const _=p*16;for(let N=0;N<16;++N)h[_+N]=w[p]+w[N]}return h}();function Kr(w){return typeof BigInt>"u"?Ol:w}function Ol(){throw new Error("BigInt not supported")}})(xo);const ST=e=>(window.location.pathname.includes("/http_proxy:http_proxy:uqbar/serve/")?`/http_proxy:http_proxy:uqbar/serve/${window.location.pathname.split("/")[3]}/${e}`:e).replace("//","/");function Ri(e){const t=document.cookie.split(";");for(let r=0;r{const n=new FileReader;n.onerror=r,n.onload=function(i){const a=i.target.result,s=new Uint8Array(a);t(s)},n.readAsArrayBuffer(e)})}class TT{constructor({nodeId:t,channelId:r,uri:n="ws://"+window.location.host,onMessage:i=()=>null,onOpen:a=()=>null,onClose:s=()=>null,onError:o=()=>null,onEncryptionReady:l=()=>null}){ft(this,"nodeId");ft(this,"channelId");ft(this,"_secret");ft(this,"_cipher");ft(this,"_decipher");ft(this,"_ws");ft(this,"_encrypt",t=>{if(this._cipher){const r=yr.random.getBytesSync(12);this._cipher.start({iv:r}),this._cipher.update(yr.util.createBuffer(t,"utf8")),this._cipher.finish();const n=this._cipher.output.getBytes(),i=this._cipher.mode.tag.getBytes();return{encrypted:n+i,nonce:r}}return null});ft(this,"_decrypt",t=>{if(!this._decipher)return null;const r=t.slice(0,-28),n=t.slice(-28,-12),i=t.slice(-12);return this._decipher.start({iv:yr.util.createBuffer(i),tag:yr.util.createBuffer(n)}),this._decipher.update(yr.util.createBuffer(r)),this._decipher.finish()?this._decipher.output.toString():null});ft(this,"send",({data:t,channelId:r=this.channelId,encrypted:n=!0,target:i={node:this.nodeId,process:this.channelId}})=>{const a=Ri(`uqbar-auth_${this.nodeId}`),s=Ri(`uqbar-ws-auth_${this.nodeId}`);if(n){if(!this._cipher)return console.error("No cipher, unable to encrypt");this._ws.send(Bu({EncryptedWsMessage:{auth_token:a,ws_auth_token:s,channel_id:r,target:{node:i.node,process:typeof i.process=="number"?{Id:i.process}:{Name:i.process}},...this._encrypt(JSON.stringify(t))}}))}else this._ws.send(Bu({WsMessage:{auth_token:a,ws_auth_token:s,channel_id:r,target:{node:i.node,process:typeof i.process=="number"?{Id:i.process}:{Name:i.process}},json:t}}))});ft(this,"fetchJson",async(t,r)=>(console.log("Fetching JSON:",t),await(await fetch(t,r)).json()));this._secret=void 0,this.channelId=r,this.nodeId=t,this._ws=new WebSocket(n),this._ws.onmessage=async d=>{if(typeof d.data=="string")i(d.data);else if(d.data instanceof Blob){const v=await xT(d.data),g=this._decrypt(v);if(g===null){console.log("Unable to decrypt message, passing through as-is");const C=new TextDecoder().decode(v);i(C)}else i(g)}else i(d.data)},this._ws.onopen=d=>{console.log(`${t}`,Ri(`uqbar-auth_${t}`),Ri(`uqbar-ws-auth_${t}`)),this._ws.send(Bu({WsRegister:{auth_token:Ri(`uqbar-auth_${t}`),ws_auth_token:Ri(`uqbar-ws-auth_${t}`),channel_id:r}})),a(d)},this._ws.onclose=s,this._ws.onerror=o;const c=yr.pki.rsa.generateKeyPair({bits:2048,e:65537}),f=c.publicKey.n.toString(16);fetch(ST("/encryptor:sys:uqbar"),{method:"POST",body:JSON.stringify({channel_id:r,public_key_hex:f})}).then(d=>d.json()).then(d=>{const{encrypted_secret:v,signed_public_key:g}=d;fetch(`/qns_indexer:qns_indexer:uqbar/node/${this.nodeId}`).then(C=>C.json()).then(C=>{const{public_key:I}=C,y=I.replace("0x","");if(!y)return;if(yr.pki.ed25519.verify({message:xo.Buffer.from(f,"hex"),signature:xo.Buffer.from(g,"hex"),publicKey:xo.Buffer.from(y,"hex")})){const S=yr.util.hexToBytes(v),B=c.privateKey.decrypt(S,"RSA-OAEP",{md:yr.md.sha256.create()});this._secret=yr.util.bytesToHex(B),this._cipher=yr.cipher.createCipher("AES-GCM",B),this._decipher=yr.cipher.createDecipher("AES-GCM",B),l(this)}else console.error("Unable to verify networking key")}).catch(console.error)}).catch(console.error)}}const bh=e=>{let t;const r=new Set,n=(l,u)=>{const c=typeof l=="function"?l(t):l;if(!Object.is(c,t)){const f=t;t=u??typeof c!="object"?c:Object.assign({},t,c),r.forEach(d=>d(t,f))}},i=()=>t,o={setState:n,getState:i,subscribe:l=>(r.add(l),()=>r.delete(l)),destroy:()=>{r.clear()}};return t=e(n,i,o),o},wT=e=>e?bh(e):bh;var iv={exports:{}},av={},sv={exports:{}},ov={};/** - * @license React - * use-sync-external-store-shim.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var da=q;function IT(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var _T=typeof Object.is=="function"?Object.is:IT,AT=da.useState,BT=da.useEffect,kT=da.useLayoutEffect,bT=da.useDebugValue;function NT(e,t){var r=t(),n=AT({inst:{value:r,getSnapshot:t}}),i=n[0].inst,a=n[1];return kT(function(){i.value=r,i.getSnapshot=t,ku(i)&&a({inst:i})},[e,r,t]),BT(function(){return ku(i)&&a({inst:i}),e(function(){ku(i)&&a({inst:i})})},[e]),bT(r),r}function ku(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!_T(e,r)}catch{return!0}}function DT(e,t){return t()}var RT=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?DT:NT;ov.useSyncExternalStore=da.useSyncExternalStore!==void 0?da.useSyncExternalStore:RT;sv.exports=ov;var LT=sv.exports;/** - * @license React - * use-sync-external-store-shim/with-selector.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Rl=q,OT=LT;function PT(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var UT=typeof Object.is=="function"?Object.is:PT,MT=OT.useSyncExternalStore,FT=Rl.useRef,VT=Rl.useEffect,jT=Rl.useMemo,KT=Rl.useDebugValue;av.useSyncExternalStoreWithSelector=function(e,t,r,n,i){var a=FT(null);if(a.current===null){var s={hasValue:!1,value:null};a.current=s}else s=a.current;a=jT(function(){function l(v){if(!u){if(u=!0,c=v,v=n(v),i!==void 0&&s.hasValue){var g=s.value;if(i(g,v))return f=g}return f=v}if(g=f,UT(c,v))return g;var C=n(v);return i!==void 0&&i(g,C)?g:(c=v,f=C)}var u=!1,c,f,d=r===void 0?null:r;return[function(){return l(t())},d===null?void 0:function(){return l(d())}]},[t,r,n,i]);var o=MT(e,a[0],a[1]);return VT(function(){s.hasValue=!0,s.value=o},[o]),KT(o),o};iv.exports=av;var zT=iv.exports;const HT=Qc(zT),{useSyncExternalStoreWithSelector:qT}=HT;function $T(e,t=e.getState,r){const n=qT(e.subscribe,e.getState,e.getServerState||e.getState,t,r);return q.useDebugValue(n),n}const Nh=e=>{const t=typeof e=="function"?wT(e):e,r=(n,i)=>$T(t,n,i);return Object.assign(r,t),r},GT=e=>e?Nh(e):Nh;function lv(e,t){let r;try{r=e()}catch{return}return{getItem:i=>{var a;const s=l=>l===null?null:JSON.parse(l,t==null?void 0:t.reviver),o=(a=r.getItem(i))!=null?a:null;return o instanceof Promise?o.then(s):s(o)},setItem:(i,a)=>r.setItem(i,JSON.stringify(a,t==null?void 0:t.replacer)),removeItem:i=>r.removeItem(i)}}const xs=e=>t=>{try{const r=e(t);return r instanceof Promise?r:{then(n){return xs(n)(r)},catch(n){return this}}}catch(r){return{then(n){return this},catch(n){return xs(n)(r)}}}},QT=(e,t)=>(r,n,i)=>{let a={getStorage:()=>localStorage,serialize:JSON.stringify,deserialize:JSON.parse,partialize:I=>I,version:0,merge:(I,y)=>({...y,...I}),...t},s=!1;const o=new Set,l=new Set;let u;try{u=a.getStorage()}catch{}if(!u)return e((...I)=>{console.warn(`[zustand persist middleware] Unable to update item '${a.name}', the given storage is currently unavailable.`),r(...I)},n,i);const c=xs(a.serialize),f=()=>{const I=a.partialize({...n()});let y;const m=c({state:I,version:a.version}).then(S=>u.setItem(a.name,S)).catch(S=>{y=S});if(y)throw y;return m},d=i.setState;i.setState=(I,y)=>{d(I,y),f()};const v=e((...I)=>{r(...I),f()},n,i);let g;const C=()=>{var I;if(!u)return;s=!1,o.forEach(m=>m(n()));const y=((I=a.onRehydrateStorage)==null?void 0:I.call(a,n()))||void 0;return xs(u.getItem.bind(u))(a.name).then(m=>{if(m)return a.deserialize(m)}).then(m=>{if(m)if(typeof m.version=="number"&&m.version!==a.version){if(a.migrate)return a.migrate(m.state,m.version);console.error("State loaded from storage couldn't be migrated since no migrate function was provided")}else return m.state}).then(m=>{var S;return g=a.merge(m,(S=n())!=null?S:v),r(g,!0),f()}).then(()=>{y==null||y(g,void 0),s=!0,l.forEach(m=>m(g))}).catch(m=>{y==null||y(void 0,m)})};return i.persist={setOptions:I=>{a={...a,...I},I.getStorage&&(u=I.getStorage())},clearStorage:()=>{u==null||u.removeItem(a.name)},getOptions:()=>a,rehydrate:()=>C(),hasHydrated:()=>s,onHydrate:I=>(o.add(I),()=>{o.delete(I)}),onFinishHydration:I=>(l.add(I),()=>{l.delete(I)})},C(),g||v},WT=(e,t)=>(r,n,i)=>{let a={storage:lv(()=>localStorage),partialize:C=>C,version:0,merge:(C,I)=>({...I,...C}),...t},s=!1;const o=new Set,l=new Set;let u=a.storage;if(!u)return e((...C)=>{console.warn(`[zustand persist middleware] Unable to update item '${a.name}', the given storage is currently unavailable.`),r(...C)},n,i);const c=()=>{const C=a.partialize({...n()});return u.setItem(a.name,{state:C,version:a.version})},f=i.setState;i.setState=(C,I)=>{f(C,I),c()};const d=e((...C)=>{r(...C),c()},n,i);let v;const g=()=>{var C,I;if(!u)return;s=!1,o.forEach(m=>{var S;return m((S=n())!=null?S:d)});const y=((I=a.onRehydrateStorage)==null?void 0:I.call(a,(C=n())!=null?C:d))||void 0;return xs(u.getItem.bind(u))(a.name).then(m=>{if(m)if(typeof m.version=="number"&&m.version!==a.version){if(a.migrate)return a.migrate(m.state,m.version);console.error("State loaded from storage couldn't be migrated since no migrate function was provided")}else return m.state}).then(m=>{var S;return v=a.merge(m,(S=n())!=null?S:d),r(v,!0),c()}).then(()=>{y==null||y(v,void 0),v=n(),s=!0,l.forEach(m=>m(v))}).catch(m=>{y==null||y(void 0,m)})};return i.persist={setOptions:C=>{a={...a,...C},C.storage&&(u=C.storage)},clearStorage:()=>{u==null||u.removeItem(a.name)},getOptions:()=>a,rehydrate:()=>g(),hasHydrated:()=>s,onHydrate:C=>(o.add(C),()=>{o.delete(C)}),onFinishHydration:C=>(l.add(C),()=>{l.delete(C)})},a.skipHydration||g(),v||d},YT=(e,t)=>"getStorage"in t||"serialize"in t||"deserialize"in t?QT(e,t):WT(e,t),XT=YT,ZT=GT()(XT((e,t)=>({games:{},handleWsMessage:r=>{const{kind:n,data:i}=JSON.parse(r);console.log(n,i),n==="game_update"&&e({games:{...t().games,[i.id]:i}})},set:e}),{name:"chess",storage:lv(()=>localStorage)}));let Dh=!1;const Rh=(e,t)=>(e.turns||0)%2===0?t===e.white:t===e.black;function JT(){const{games:e,handleWsMessage:t,set:r}=ZT(),[n,i]=q.useState("new"),[a,s]=q.useState(""),o=q.useMemo(()=>e[n]?{...e[n],game:new e2(e[n].board)}:void 0,[e,n]),l=q.useMemo(()=>((o==null?void 0:o.turns)||0)%2===0?`${o==null?void 0:o.white} (white)`:`${o==null?void 0:o.black} (black)`,[o]);q.useEffect(()=>{Dh||(Dh=!0,new TT({nodeId:window.our.node,channelId:window.our.process,onMessage:t})),fetch("/chess:chess:uqbar/games").then(v=>v.json()).then(v=>{r({games:v})}).catch(console.error)},[]);const u=q.useCallback(async v=>{v.preventDefault();try{const g=await fetch("/chess:chess:uqbar/games",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:a})}).then(I=>{if(I.status===409)throw e[a]?i(a):alert("Game already exists, please refresh the page and select it."),new Error("Game already exists");if(I.status===503)throw alert(`${a} may be offline, please confirm it is online and try again.`),new Error("Player offline");if(I.status===400)throw alert("Please enter a valid player ID"),new Error("Invalid player ID");if(I.status>399)throw alert("There was an error creating the game. Please try again."),new Error("Error creating game");return I.json()}),C={...e};C[g.id]=g,r({games:C}),i(a),s("")}catch(g){console.error(g)}},[e,a,s,r]),c=q.useCallback((v,g)=>{if(!o||!Rh(o,window.our.node))return!1;const C={from:v,to:g,promotion:"q"},I={...o};if(I.game.move(C)===null)return!1;I.board=I.game.fen();const m={...e};return m[o.id]=I,r({games:m}),fetch("/chess:chess:uqbar/games",{method:"PUT",body:JSON.stringify({id:o.id,move:v+g})}).then(S=>S.json()).then(S=>{const B={...e};B[o.id]=S,r({games:B})}).catch(S=>{console.error(S),alert("There was an error making your move. Please try again");const B={...e},R={...o};R.game.undo(),B[o.id]=R,r({games:B})}),!0},[o,e,r]),f=q.useCallback(v=>{v.preventDefault(),v.stopPropagation(),o&&window.confirm("Are you sure you want to resign this game?")&&fetch(`/chess:chess:uqbar/games?id=${o.id}`,{method:"DELETE"}).then(g=>g.json()).then(g=>{const C={...e};C[o.id]=g,r({games:C})}).catch(g=>{console.error(g),alert("There was an error resigning the game. Please try again")})},[o]),d=q.useCallback(async v=>{if(v.preventDefault(),v.stopPropagation(),!!o)try{const g=await fetch("/chess:chess:uqbar/games",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:o.id})}).then(I=>I.json()),C={...e};C[g.id]=g,r({games:C})}catch(g){console.error(g),alert("You could not create the game. Please make sure your current game with this player (if any) has ended and try again.")}},[o]);return D.jsx("div",{className:"flex flex-col justify-center items-center",children:D.jsxs("div",{className:"flex flex-col justify-center",style:{maxHeight:"100vh",maxWidth:"800px",width:"100%",position:"relative"},children:[D.jsx("a",{href:"/",className:"absolute top-6 left-0 m-4",style:{fontSize:24,color:"white"},onClick:v=>{v.preventDefault(),window.history.back()},children:"â—€ Back"}),D.jsx("h1",{className:"m-4",children:"Chess by Uqbar"}),D.jsxs("div",{className:"flex flex-row justify-center items-center h-screen border rounded",children:[Object.keys(e).length>0&&D.jsxs("div",{className:"flex flex-col border-r",style:{width:"25%",height:"100%"},children:[D.jsx("h3",{className:"m-2",children:"Games"}),D.jsx("button",{className:"bg-green-600 hover:bg-green-800 text-white font-bold py-2 px-4 m-2 rounded",onClick:()=>i("new"),children:"New"}),D.jsx("div",{className:"flex flex-col overflow-scroll",children:Object.values(e).map(v=>D.jsx("div",{onClick:()=>i(v==null?void 0:v.id),className:`game-entry m-2 ${n!==(v==null?void 0:v.id)&&Rh(v,window.our.node)?"is-turn":""} ${n===(v==null?void 0:v.id)?"selected":""} ${v!=null&&v.ended?"ended":""}`,children:v==null?void 0:v.id},v==null?void 0:v.id))})]}),D.jsx("div",{className:"flex flex-col justify-center items-center",style:{width:"75%"},children:n==="new"||!o?D.jsxs(D.Fragment,{children:[D.jsx("h2",{className:"mb-2",children:"Start New Game"}),D.jsx("h4",{className:"mb-2",children:"(game creator will be white)"}),D.jsxs("form",{onSubmit:u,className:"flex flex-col justify-center mb-40",style:{maxWidth:400},children:[D.jsx("label",{className:"mb-2",style:{alignSelf:"flex-start",fontWeight:"600"},children:"Player ID"}),D.jsx("input",{className:"border rounded p-2 mb-2",style:{color:"black"},type:"text",placeholder:"Player ID",value:a,onChange:v=>s(v.target.value)}),D.jsx("button",{className:"bg-green-600 hover:bg-green-800 text-white font-bold py-2 px-4 rounded",type:"submit",children:"Start Game"})]})]}):D.jsxs(D.Fragment,{children:[D.jsxs("div",{className:"flex flex-row justify-between items-center w-full px-4 pb-2",children:[D.jsx("h3",{children:n}),D.jsx("h4",{children:o!=null&&o.ended?"Game Ended":`Turn: ${l}`}),o!=null&&o.ended?D.jsx("button",{className:"bg-green-600 hover:bg-green-800 text-white font-bold py-1 px-4 rounded",onClick:d,children:"Rematch"}):D.jsx("button",{className:"bg-green-600 hover:bg-green-800 text-white font-bold py-1 px-4 rounded",onClick:f,children:"Resign"})]}),D.jsx(TC,{position:o==null?void 0:o.game.fen(),onPieceDrop:c,boardOrientation:(o==null?void 0:o.white)===window.our.node?"white":"black"})]})})]})]})})}bu.createRoot(document.getElementById("root")).render(D.jsx(bv.StrictMode,{children:D.jsx(JT,{})})); diff --git a/modules/chess/src/lib.rs b/modules/chess/src/lib.rs index 2ea41068..b9ad4c91 100644 --- a/modules/chess/src/lib.rs +++ b/modules/chess/src/lib.rs @@ -1,19 +1,22 @@ -cargo_component_bindings::generate!(); - -use bindings::component::uq_process::types::*; -use bindings::{ - get_payload, print_to_terminal, receive, send_and_await_response, send_request, send_requests, - send_response, Guest, -}; use serde::{Deserialize, Serialize}; use serde_json::json; use std::collections::HashMap; extern crate base64; extern crate pleco; use pleco::Board; +use uqbar_process_lib::uqbar::process::standard as wit; +use uqbar_process_lib::{ + get_payload, get_typed_state, println, receive, set_state, Address, Message, Payload, Request, + Response, +}; -#[allow(dead_code)] -mod process_lib; +wit_bindgen::generate!({ + path: "../../wit", + world: "process", + exports: { + world: Component, + }, +}); struct Component; @@ -82,36 +85,33 @@ fn json_game(game: &Game) -> serde_json::Value { }) } -fn send_http_response(status: u16, headers: HashMap, payload_bytes: Vec) { - send_response( - &Response { - inherit: false, - ipc: serde_json::json!({ +fn send_http_response( + status: u16, + headers: HashMap, + payload_bytes: Vec, +) -> anyhow::Result<()> { + Response::new() + .ipc_bytes( + serde_json::json!({ "status": status, "headers": headers, }) .to_string() .as_bytes() .to_vec(), - metadata: None, - }, - Some(&Payload { + ) + .payload(Payload { mime: Some("application/octet-stream".to_string()), bytes: payload_bytes, - }), - ) + }) + .send() } -fn send_ws_update(our: Address, game: Game) { - send_request( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("encryptor:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: None, - ipc: serde_json::json!({ +fn send_ws_update(our: Address, game: Game) -> anyhow::Result<()> { + Request::new() + .target(Address::new(&our.node, "encryptor:sys:uqbar").unwrap())? + .ipc_bytes( + serde_json::json!({ "EncryptAndForwardAction": { "channel_id": our.process.to_string(), "forward_to": { @@ -136,10 +136,8 @@ fn send_ws_update(our: Address, game: Game) { .to_string() .as_bytes() .to_vec(), - metadata: None, - }, - None, - Some(&Payload { + ) + .payload(Payload { mime: Some("application/json".to_string()), bytes: serde_json::json!({ "kind": "game_update", @@ -148,8 +146,8 @@ fn send_ws_update(our: Address, game: Game) { .to_string() .as_bytes() .to_vec(), - }), - ); + }) + .send() } fn response_success() -> bool { @@ -166,33 +164,25 @@ fn response_success() -> bool { fn save_chess_state(state: ChessState) { let stored_state = convert_state(state); - process_lib::set_state::(&stored_state); + set_state(&bincode::serialize(&stored_state).unwrap()); } -const CHESS_PAGE: &str = include_str!("chess.html"); -const CHESS_JS: &str = include_str!("index.js"); -const CHESS_CSS: &str = include_str!("index.css"); +const CHESS_PAGE: &str = include_str!("../pkg/chess.html"); +const CHESS_JS: &str = include_str!("../pkg/index.js"); +const CHESS_CSS: &str = include_str!("../pkg/index.css"); impl Guest for Component { - fn init(our: Address) { - print_to_terminal(0, "CHESS: start"); + fn init(our: String) { + let our = Address::from_str(&our).unwrap(); + println!("CHESS: start"); - let bindings_address = Address { - node: our.node.clone(), - process: ProcessId::from_str("http_server:sys:uqbar").unwrap(), - }; - - // , option> - let http_endpoint_binding_requests: [(Address, Request, Option, Option); - 2] = [ - ( - bindings_address.clone(), - Request { - inherit: false, - expects_response: None, - ipc: json!({ + for path in ["/", "/games"] { + Request::new() + .target(Address::new(&our.node, "http_server:sys:uqbar").unwrap()).unwrap() + .ipc_bytes( + serde_json::json!({ "BindPath": { - "path": "/", + "path": path, "authenticated": true, "local_only": false } @@ -200,35 +190,12 @@ impl Guest for Component { .to_string() .as_bytes() .to_vec(), - metadata: None, - }, - None, - None, - ), - ( - bindings_address.clone(), - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": "/games", - "authenticated": true, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ), - ]; - send_requests(&http_endpoint_binding_requests); + ) + .send(); + } - let mut state: ChessState = match process_lib::get_state::() { + let mut state: ChessState = match get_typed_state(|bytes| Ok(bincode::deserialize::(bytes)?)) + { Some(state) => { let mut games = HashMap::new(); for (id, game) in state.games { @@ -238,7 +205,7 @@ impl Guest for Component { Game { id: game.id.clone(), turns: game.turns, - board: board, + board, white: game.white.clone(), black: game.black.clone(), ended: game.ended, @@ -258,7 +225,6 @@ impl Guest for Component { ); } } - ChessState { games, records: state.records, @@ -272,693 +238,597 @@ impl Guest for Component { loop { let Ok((source, message)) = receive() else { - print_to_terminal(0, "chess: got network error"); + println!("chess: got network error"); continue; }; let Message::Request(request) = message else { - print_to_terminal(1, "chess: got unexpected Response"); + println!("chess: got unexpected Response"); continue; }; - let message_json: serde_json::Value = match serde_json::from_slice(&request.ipc) { - Ok(v) => v, - Err(_) => { - print_to_terminal(1, "chess: failed to parse ipc JSON, skipping"); - continue; - } - }; - - print_to_terminal(1, &format!("chess: parsed ipc JSON: {:?}", message_json)); - - if source.process.to_string() == "chess:chess:uqbar" { - let action = message_json["action"].as_str().unwrap_or(""); - let game_id = source.node.clone(); - - match action { - "new_game" => { - // make a new game with source.node if the current game has ended - if let Some(game) = state.games.get(&game_id) { - if !game.ended { - send_response( - &Response { - inherit: false, - ipc: vec![], - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: "conflict".as_bytes().to_vec(), - }), - ); - continue; - } - } - let game = Game { - id: game_id.clone(), - turns: 0, - board: Board::start_pos(), - white: message_json["white"] - .as_str() - .unwrap_or(game_id.as_str()) - .to_string(), - black: message_json["black"] - .as_str() - .unwrap_or(our.node.as_str()) - .to_string(), - ended: false, - }; - state.games.insert(game_id.clone(), game.clone()); - - send_ws_update(our.clone(), game.clone()); - - save_chess_state(state.clone()); - - send_response( - &Response { - inherit: false, - ipc: vec![], - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: "success".as_bytes().to_vec(), - }), - ); - continue; - } - "make_move" => { - // check the move and then update if correct and send WS update - let Some(game) = state.games.get_mut(&game_id) else { - send_response( - &Response { - inherit: false, - ipc: vec![], - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: "not found".as_bytes().to_vec(), - }), - ); - continue; - }; - let valid_move = game - .board - .apply_uci_move(message_json["move"].as_str().unwrap_or("")); - if valid_move { - game.turns += 1; - let checkmate = game.board.checkmate(); - let draw = game.board.stalemate(); - - if checkmate || draw { - game.ended = true; - let winner = if checkmate { - if game.turns % 2 == 1 { - game.white.clone() - } else { - game.black.clone() - } - } else { - "".to_string() - }; - - // update the records - if draw { - if let Some(record) = state.records.get_mut(&game.id) { - record.2 += 1; - } else { - state.records.insert(game.id.clone(), (0, 0, 1)); - } - } else { - if let Some(record) = state.records.get_mut(&game.id) { - if winner == our.node { - record.0 += 1; - } else { - record.1 += 1; - } - } else { - if winner == our.node { - state.records.insert(game.id.clone(), (1, 0, 0)); - } else { - state.records.insert(game.id.clone(), (0, 1, 0)); - } - } - } - } - - send_ws_update(our.clone(), game.clone()); - save_chess_state(state.clone()); - - send_response( - &Response { - inherit: false, - ipc: vec![], - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: "success".as_bytes().to_vec(), - }), - ); - continue; - } else { - send_response( - &Response { - inherit: false, - ipc: vec![], - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: "invalid move".as_bytes().to_vec(), - }), - ); - continue; - } - } - "end_game" => { - // end the game and send WS update, update the standings - let Some(game) = state.games.get_mut(&game_id) else { - send_response( - &Response { - inherit: false, - ipc: vec![], - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: "not found".as_bytes().to_vec(), - }), - ); - continue; - }; - - game.ended = true; - - if let Some(record) = state.records.get_mut(&game.id) { - record.0 += 1; - } else { - state.records.insert(game.id.clone(), (1, 0, 0)); - } - - send_ws_update(our.clone(), game.clone()); - save_chess_state(state.clone()); - - send_response( - &Response { - inherit: false, - ipc: vec![], - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: "success".as_bytes().to_vec(), - }), - ); - } - _ => { - print_to_terminal(1, "chess: got unexpected action"); - continue; - } - } - } else if source.process.to_string() == "http_server:sys:uqbar" { - let path = message_json["path"].as_str().unwrap_or(""); - let method = message_json["method"].as_str().unwrap_or(""); - - let mut default_headers = HashMap::new(); - default_headers.insert("Content-Type".to_string(), "text/html".to_string()); - // Handle incoming http - match path { - "/" => { - send_http_response( - 200, - default_headers.clone(), - CHESS_PAGE - .replace("${node}", &our.node) - .replace("${process}", &our.process.to_string()) - .replace("${js}", CHESS_JS) - .replace("${css}", CHESS_CSS) - .to_string() - .as_bytes() - .to_vec(), - ); - } - "/games" => { - match method { - "GET" => { - send_http_response( - 200, - { - let mut headers = default_headers.clone(); - headers.insert( - "Content-Type".to_string(), - "application/json".to_string(), - ); - headers - }, - { - let mut json_games: HashMap = - HashMap::new(); - for (id, game) in &state.games { - json_games.insert(id.to_string(), json_game(&game)); - } - json!(json_games).to_string().as_bytes().to_vec() - }, - ); - } - "POST" => { - // create a new game - if let Some(payload) = get_payload() { - if let Ok(payload_json) = - serde_json::from_slice::(&payload.bytes) - { - let game_id = - String::from(payload_json["id"].as_str().unwrap_or("")); - if game_id == "" { - send_http_response( - 400, - default_headers.clone(), - "Bad Request".to_string().as_bytes().to_vec(), - ); - continue; - } - - if let Some(game) = state.games.get(&game_id) { - if !game.ended { - send_http_response( - 409, - default_headers.clone(), - "Conflict".to_string().as_bytes().to_vec(), - ); - continue; - } - } - - let white = payload_json["white"] - .as_str() - .unwrap_or(our.node.as_str()) - .to_string(); - let black = payload_json["black"] - .as_str() - .unwrap_or(game_id.as_str()) - .to_string(); - - let response = send_and_await_response( - &Address { - node: game_id.clone(), - process: ProcessId::from_str("chess:chess:uqbar") - .unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(30), // TODO check this! - ipc: serde_json::json!({ - "action": "new_game", - "white": white.clone(), - "black": black.clone(), - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - ); - - match response { - Ok(_reponse) => { - if !response_success() { - send_http_response( - 503, - default_headers.clone(), - "Service Unavailable" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - } - // create a new game - let game = Game { - id: game_id.clone(), - turns: 0, - board: Board::start_pos(), - white: white.clone(), - black: black.clone(), - ended: false, - }; - state.games.insert(game_id.clone(), game.clone()); - - save_chess_state(state.clone()); - - send_http_response( - 200, - { - let mut headers = default_headers.clone(); - headers.insert( - "Content-Type".to_string(), - "application/json".to_string(), - ); - headers - }, - json_game(&game) - .to_string() - .as_bytes() - .to_vec(), - ); - } - Err(_) => { - send_http_response( - 503, - default_headers.clone(), - "Service Unavailable" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } - continue; - } - } - - send_http_response( - 400, - default_headers.clone(), - "Bad Request".to_string().as_bytes().to_vec(), - ); - } - "PUT" => { - // make a move - if let Some(payload) = get_payload() { - print_to_terminal( - 1, - format!( - "payload: {}", - String::from_utf8(payload.bytes.clone()) - .unwrap_or("".to_string()) - ) - .as_str(), - ); - if let Ok(payload_json) = - serde_json::from_slice::(&payload.bytes) - { - let game_id = - String::from(payload_json["id"].as_str().unwrap_or("")); - - if game_id == "" { - send_http_response( - 400, - default_headers.clone(), - "No game ID".to_string().as_bytes().to_vec(), - ); - continue; - } - - if let Some(game) = state.games.get_mut(&game_id) { - if game.turns % 2 == 0 && game.white != our.node { - send_http_response( - 403, - default_headers.clone(), - "Forbidden".to_string().as_bytes().to_vec(), - ); - continue; - } else if game.turns % 2 == 1 && game.black != our.node - { - send_http_response( - 403, - default_headers.clone(), - "Forbidden".to_string().as_bytes().to_vec(), - ); - continue; - } else if game.ended { - send_http_response( - 409, - default_headers.clone(), - "Conflict".to_string().as_bytes().to_vec(), - ); - continue; - } - - let move_str = - payload_json["move"].as_str().unwrap_or(""); - let valid_move = game.board.apply_uci_move(move_str); - if valid_move { - // send the move to the other player - // check if the game is over - // if so, update the records - let response = send_and_await_response( - &Address { - node: game_id.clone(), - process: ProcessId::from_str( - "chess:chess:uqbar", - ) - .unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(30), // TODO check this! - ipc: serde_json::json!({ - "action": "make_move", - "move": move_str, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - ); - - match response { - Ok(_reponse) => { - if !response_success() { - send_http_response( - 503, - default_headers.clone(), - "Service Unavailable" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - } - // update the game - game.turns += 1; - let checkmate = game.board.checkmate(); - let draw = game.board.stalemate(); - - if checkmate || draw { - game.ended = true; - let winner = if checkmate { - if game.turns % 2 == 1 { - game.white.clone() - } else { - game.black.clone() - } - } else { - "".to_string() - }; - - // update the records - if draw { - if let Some(record) = - state.records.get_mut(&game.id) - { - record.2 += 1; - } else { - state.records.insert( - game.id.clone(), - (0, 0, 1), - ); - } - } else { - if let Some(record) = - state.records.get_mut(&game.id) - { - if winner == our.node { - record.0 += 1; - } else { - record.1 += 1; - } - } else { - if winner == our.node { - state.records.insert( - game.id.clone(), - (1, 0, 0), - ); - } else { - state.records.insert( - game.id.clone(), - (0, 1, 0), - ); - } - } - } - } - - let game = game.clone(); - save_chess_state(state.clone()); - // return the game - send_http_response( - 200, - { - let mut headers = - default_headers.clone(); - headers.insert( - "Content-Type".to_string(), - "application/json".to_string(), - ); - headers - }, - json_game(&game) - .to_string() - .as_bytes() - .to_vec(), - ); - } - Err(_) => { - send_http_response( - 503, - default_headers.clone(), - "Service Unavailable" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } - - continue; - } - } - } - } - - print_to_terminal(0, "never got a response"); - send_http_response( - 400, - default_headers.clone(), - "Bad Request".to_string().as_bytes().to_vec(), - ); - } - "DELETE" => { - let game_id = message_json["query_params"]["id"] - .as_str() - .unwrap_or("") - .to_string(); - if game_id == "" { - send_http_response( - 400, - default_headers.clone(), - "Bad Request".to_string().as_bytes().to_vec(), - ); - continue; - } else { - if let Some(game) = state.games.get_mut(&game_id) { - let response = send_and_await_response( - &Address { - node: game_id.clone(), - process: ProcessId::from_str("chess:chess:uqbar") - .unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(30), // TODO check this! - ipc: serde_json::json!({ - "action": "end_game", - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - ); - - match response { - Ok(_response) => { - if !response_success() { - send_http_response( - 503, - default_headers.clone(), - "Service Unavailable" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - } - - game.ended = true; - - if let Some(record) = - state.records.get_mut(&game.id) - { - record.1 += 1; - } else { - state - .records - .insert(game.id.clone(), (0, 1, 0)); - } - - let game = game.clone(); - save_chess_state(state.clone()); - - // return the game - send_http_response( - 200, - { - let mut headers = default_headers.clone(); - headers.insert( - "Content-Type".to_string(), - "application/json".to_string(), - ); - headers - }, - json_game(&game) - .to_string() - .as_bytes() - .to_vec(), - ); - } - Err(_) => { - send_http_response( - 503, - default_headers.clone(), - "Service Unavailable" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } - - continue; - } - } - // end a game - } - _ => { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - continue; - } - } - } - _ => { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - continue; - } + match handle_request(&our, &source, &request, &mut state) { + Ok(_) => {} + Err(e) => { + println!("chess: error handling request: {:?}", e); } } } } } + +fn handle_request( + our: &Address, + source: &Address, + request: &wit::Request, + state: &mut ChessState, +) -> anyhow::Result<()> { + let message_json: serde_json::Value = match serde_json::from_slice(&request.ipc) { + Ok(v) => v, + Err(_) => return Err(anyhow::anyhow!("chess: failed to parse ipc JSON, skipping")), + }; + + // print_to_terminal(1, &format!("chess: parsed ipc JSON: {:?}", message_json)); + + if source.process == "chess:chess:uqbar" { + let action = message_json["action"].as_str().unwrap_or(""); + let game_id = source.node.clone(); + + match action { + "new_game" => { + // make a new game with source.node if the current game has ended + if let Some(game) = state.games.get(&game_id) { + if !game.ended { + return Response::new() + .ipc_bytes(vec![]) + .payload(Payload { + mime: Some("application/octet-stream".to_string()), + bytes: "conflict".as_bytes().to_vec(), + }) + .send() + } + } + let game = Game { + id: game_id.clone(), + turns: 0, + board: Board::start_pos(), + white: message_json["white"] + .as_str() + .unwrap_or(game_id.as_str()) + .to_string(), + black: message_json["black"] + .as_str() + .unwrap_or(our.node.as_str()) + .to_string(), + ended: false, + }; + state.games.insert(game_id.clone(), game.clone()); + + send_ws_update(our.clone(), game.clone()); + + save_chess_state(state.clone()); + + Response::new() + .ipc_bytes(vec![]) + .payload(Payload { + mime: Some("application/octet-stream".to_string()), + bytes: "success".as_bytes().to_vec(), + }) + .send() + } + "make_move" => { + // check the move and then update if correct and send WS update + let Some(game) = state.games.get_mut(&game_id) else { + return Response::new() + .ipc_bytes(vec![]) + .payload(Payload { + mime: Some("application/octet-stream".to_string()), + bytes: "not found".as_bytes().to_vec(), + }) + .send() + }; + let valid_move = game + .board + .apply_uci_move(message_json["move"].as_str().unwrap_or("")); + if valid_move { + game.turns += 1; + let checkmate = game.board.checkmate(); + let draw = game.board.stalemate(); + + if checkmate || draw { + game.ended = true; + let winner = if checkmate { + if game.turns % 2 == 1 { + game.white.clone() + } else { + game.black.clone() + } + } else { + "".to_string() + }; + + // update the records + if draw { + if let Some(record) = state.records.get_mut(&game.id) { + record.2 += 1; + } else { + state.records.insert(game.id.clone(), (0, 0, 1)); + } + } else { + if let Some(record) = state.records.get_mut(&game.id) { + if winner == our.node { + record.0 += 1; + } else { + record.1 += 1; + } + } else { + if winner == our.node { + state.records.insert(game.id.clone(), (1, 0, 0)); + } else { + state.records.insert(game.id.clone(), (0, 1, 0)); + } + } + } + } + + send_ws_update(our.clone(), game.clone()); + save_chess_state(state.clone()); + + Response::new() + .ipc_bytes(vec![]) + .payload(Payload { + mime: Some("application/octet-stream".to_string()), + bytes: "success".as_bytes().to_vec(), + }) + .send() + } else { + Response::new() + .ipc_bytes(vec![]) + .payload(Payload { + mime: Some("application/octet-stream".to_string()), + bytes: "invalid move".as_bytes().to_vec(), + }) + .send() + } + } + "end_game" => { + // end the game and send WS update, update the standings + let Some(game) = state.games.get_mut(&game_id) else { + return Response::new() + .ipc_bytes(vec![]) + .payload(Payload { + mime: Some("application/octet-stream".to_string()), + bytes: "not found".as_bytes().to_vec(), + }) + .send() + }; + + game.ended = true; + + if let Some(record) = state.records.get_mut(&game.id) { + record.0 += 1; + } else { + state.records.insert(game.id.clone(), (1, 0, 0)); + } + + send_ws_update(our.clone(), game.clone()); + save_chess_state(state.clone()); + + Response::new() + .ipc_bytes(vec![]) + .payload(Payload { + mime: Some("application/octet-stream".to_string()), + bytes: "success".as_bytes().to_vec(), + }) + .send() + } + _ => return Err(anyhow::anyhow!("chess: got unexpected action")), + } + } else if source.process.to_string() == "http_server:sys:uqbar" { + let path = message_json["path"].as_str().unwrap_or(""); + let method = message_json["method"].as_str().unwrap_or(""); + + let mut default_headers = HashMap::new(); + default_headers.insert("Content-Type".to_string(), "text/html".to_string()); + // Handle incoming http + match path { + "/" => { + return send_http_response( + 200, + default_headers.clone(), + CHESS_PAGE + .replace("${node}", &our.node) + .replace("${process}", &our.process.to_string()) + .replace("${js}", CHESS_JS) + .replace("${css}", CHESS_CSS) + .to_string() + .as_bytes() + .to_vec(), + ); + } + "/games" => { + match method { + "GET" => { + return send_http_response( + 200, + { + let mut headers = default_headers.clone(); + headers.insert( + "Content-Type".to_string(), + "application/json".to_string(), + ); + headers + }, + { + let mut json_games: HashMap = + HashMap::new(); + for (id, game) in &state.games { + json_games.insert(id.to_string(), json_game(&game)); + } + json!(json_games).to_string().as_bytes().to_vec() + }, + ); + } + "POST" => { + // create a new game + if let Some(payload) = get_payload() { + if let Ok(payload_json) = + serde_json::from_slice::(&payload.bytes) + { + let game_id = + String::from(payload_json["id"].as_str().unwrap_or("")); + if game_id == "" { + return send_http_response( + 400, + default_headers.clone(), + "Bad Request".to_string().as_bytes().to_vec(), + ); + } + + if let Some(game) = state.games.get(&game_id) { + if !game.ended { + return send_http_response( + 409, + default_headers.clone(), + "Conflict".to_string().as_bytes().to_vec(), + ); + } + } + + let white = payload_json["white"] + .as_str() + .unwrap_or(our.node.as_str()) + .to_string(); + let black = payload_json["black"] + .as_str() + .unwrap_or(game_id.as_str()) + .to_string(); + + let response = Request::new() + .target(Address::new(&game_id, "chess:chess:uqbar")?)? + .ipc_bytes( + serde_json::json!({ + "action": "new_game", + "white": white.clone(), + "black": black.clone(), + }) + .to_string() + .as_bytes() + .to_vec(), + ) + .send_and_await_response(30)?; + + match response { + Ok(_response) => { + if !response_success() { + return send_http_response( + 503, + default_headers.clone(), + "Service Unavailable" + .to_string() + .as_bytes() + .to_vec(), + ); + } + // create a new game + let game = Game { + id: game_id.clone(), + turns: 0, + board: Board::start_pos(), + white: white.clone(), + black: black.clone(), + ended: false, + }; + state.games.insert(game_id.clone(), game.clone()); + + save_chess_state(state.clone()); + + return send_http_response( + 200, + { + let mut headers = default_headers.clone(); + headers.insert( + "Content-Type".to_string(), + "application/json".to_string(), + ); + headers + }, + json_game(&game).to_string().as_bytes().to_vec(), + ); + } + Err(_) => { + return send_http_response( + 503, + default_headers.clone(), + "Service Unavailable".to_string().as_bytes().to_vec(), + ) + } + } + } + } + return send_http_response( + 400, + default_headers.clone(), + "Bad Request".to_string().as_bytes().to_vec(), + ); + } + "PUT" => { + // make a move + if let Some(payload) = get_payload() { + if let Ok(payload_json) = + serde_json::from_slice::(&payload.bytes) + { + let game_id = + String::from(payload_json["id"].as_str().unwrap_or("")); + + if game_id == "" { + return send_http_response( + 400, + default_headers.clone(), + "No game ID".to_string().as_bytes().to_vec(), + ); + } + + if let Some(game) = state.games.get_mut(&game_id) { + if game.turns % 2 == 0 && game.white != our.node { + return send_http_response( + 403, + default_headers.clone(), + "Forbidden".to_string().as_bytes().to_vec(), + ); + } else if game.turns % 2 == 1 && game.black != our.node { + return send_http_response( + 403, + default_headers.clone(), + "Forbidden".to_string().as_bytes().to_vec(), + ); + } else if game.ended { + return send_http_response( + 409, + default_headers.clone(), + "Conflict".to_string().as_bytes().to_vec(), + ); + } + + let move_str = payload_json["move"].as_str().unwrap_or(""); + let valid_move = game.board.apply_uci_move(move_str); + if valid_move { + // send the move to the other player + // check if the game is over + // if so, update the records + let response = Request::new() + .target(Address::new(&game_id, "chess:chess:uqbar")?)? + .ipc_bytes( + serde_json::json!({ + "action": "make_move", + "move": move_str, + }) + .to_string() + .as_bytes() + .to_vec(), + ) + .send_and_await_response(30)?; + + match response { + Ok(_response) => { + if !response_success() { + return send_http_response( + 503, + default_headers.clone(), + "Service Unavailable" + .to_string() + .as_bytes() + .to_vec(), + ); + } + // update the game + game.turns += 1; + let checkmate = game.board.checkmate(); + let draw = game.board.stalemate(); + + if checkmate || draw { + game.ended = true; + let winner = if checkmate { + if game.turns % 2 == 1 { + game.white.clone() + } else { + game.black.clone() + } + } else { + "".to_string() + }; + + // update the records + if draw { + if let Some(record) = + state.records.get_mut(&game.id) + { + record.2 += 1; + } else { + state + .records + .insert(game.id.clone(), (0, 0, 1)); + } + } else { + if let Some(record) = + state.records.get_mut(&game.id) + { + if winner == our.node { + record.0 += 1; + } else { + record.1 += 1; + } + } else { + if winner == our.node { + state.records.insert( + game.id.clone(), + (1, 0, 0), + ); + } else { + state.records.insert( + game.id.clone(), + (0, 1, 0), + ); + } + } + } + } + + let game = game.clone(); + save_chess_state(state.clone()); + // return the game + return send_http_response( + 200, + { + let mut headers = default_headers.clone(); + headers.insert( + "Content-Type".to_string(), + "application/json".to_string(), + ); + headers + }, + json_game(&game) + .to_string() + .as_bytes() + .to_vec(), + ); + } + Err(_) => { + return send_http_response( + 503, + default_headers.clone(), + "Service Unavailable" + .to_string() + .as_bytes() + .to_vec(), + ) + } + } + } + } + } + } + + println!("chess: never got a response"); + return send_http_response( + 400, + default_headers.clone(), + "Bad Request".to_string().as_bytes().to_vec(), + ); + } + "DELETE" => { + let game_id = message_json["query_params"]["id"] + .as_str() + .unwrap_or("") + .to_string(); + if game_id == "" { + return send_http_response( + 400, + default_headers.clone(), + "Bad Request".to_string().as_bytes().to_vec(), + ); + } else { + let Some(game) = state.games.get_mut(&game_id) else { + return send_http_response( + 400, + default_headers.clone(), + "Bad Request".to_string().as_bytes().to_vec(), + ); + }; + let response = Request::new() + .target(Address::new(&game_id, "chess:chess:uqbar")?)? + .ipc_bytes( + serde_json::json!({ + "action": "end_game", + }) + .to_string() + .as_bytes() + .to_vec(), + ) + .send_and_await_response(30)?; + + match response { + Ok(_response) => { + if !response_success() { + return send_http_response( + 503, + default_headers.clone(), + "Service Unavailable".to_string().as_bytes().to_vec(), + ); + } + + game.ended = true; + + if let Some(record) = state.records.get_mut(&game.id) { + record.1 += 1; + } else { + state.records.insert(game.id.clone(), (0, 1, 0)); + } + + let game = game.clone(); + save_chess_state(state.clone()); + + // return the game + return send_http_response( + 200, + { + let mut headers = default_headers.clone(); + headers.insert( + "Content-Type".to_string(), + "application/json".to_string(), + ); + headers + }, + json_game(&game).to_string().as_bytes().to_vec(), + ); + } + Err(_) => { + return send_http_response( + 503, + default_headers.clone(), + "Service Unavailable".to_string().as_bytes().to_vec(), + ); + } + } + } + } + _ => { + return send_http_response( + 404, + default_headers.clone(), + "Not Found".to_string().as_bytes().to_vec(), + ) + } + } + } + _ => { + return send_http_response( + 404, + default_headers.clone(), + "Not Found".to_string().as_bytes().to_vec(), + ) + } + } + } else { + return Err(anyhow::anyhow!("chess: got request from unexpected source")); + } +} diff --git a/modules/chess/src/process_lib.rs b/modules/chess/src/process_lib.rs deleted file mode 120000 index 77367fe0..00000000 --- a/modules/chess/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../src/process_lib.rs \ No newline at end of file From c7a59419a48497d84648b42b056329f6b073a192 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 22:05:08 -0500 Subject: [PATCH 38/40] orgs gets the axe --- modules/orgs/Cargo.lock | 460 -------- modules/orgs/Cargo.toml | 31 - modules/orgs/pkg/manifest.json | 12 - modules/orgs/pkg/metadata.json | 5 - modules/orgs/src/lib.rs | 1763 ------------------------------- modules/orgs/src/process_lib.rs | 1 - 6 files changed, 2272 deletions(-) delete mode 100644 modules/orgs/Cargo.lock delete mode 100644 modules/orgs/Cargo.toml delete mode 100644 modules/orgs/pkg/manifest.json delete mode 100644 modules/orgs/pkg/metadata.json delete mode 100644 modules/orgs/src/lib.rs delete mode 120000 modules/orgs/src/process_lib.rs diff --git a/modules/orgs/Cargo.lock b/modules/orgs/Cargo.lock deleted file mode 100644 index e8ad0049..00000000 --- a/modules/orgs/Cargo.lock +++ /dev/null @@ -1,460 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "cargo-component-bindings" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#36c221e41db3e87dec4c82eadcb9bc8f37626533" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.1.0" -source = "git+https://github.com/bytecodealliance/cargo-component#36c221e41db3e87dec4c82eadcb9bc8f37626533" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown", - "serde", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "orgs" -version = "0.1.0" -dependencies = [ - "anyhow", - "base64", - "bincode", - "cargo-component-bindings", - "serde", - "serde_json", - "wit-bindgen", -] - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "proc-macro2" -version = "1.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" -dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "serde" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "spdx" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" -dependencies = [ - "smallvec", -] - -[[package]] -name = "syn" -version = "2.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasm-encoder" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-metadata" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" -dependencies = [ - "anyhow", - "indexmap", - "serde", - "serde_json", - "spdx", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.112.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" -dependencies = [ - "indexmap", - "semver", -] - -[[package]] -name = "wit-bindgen" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" -dependencies = [ - "bitflags 2.4.0", - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" -dependencies = [ - "anyhow", - "wit-component", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" -dependencies = [ - "anyhow", - "heck", - "wasm-metadata", - "wit-bindgen-core", - "wit-bindgen-rust-lib", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-lib" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" -dependencies = [ - "heck", - "wit-bindgen-core", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" -dependencies = [ - "anyhow", - "proc-macro2", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", -] - -[[package]] -name = "wit-component" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" -dependencies = [ - "anyhow", - "bitflags 2.4.0", - "indexmap", - "log", - "serde", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "pulldown-cmark", - "semver", - "unicode-xid", - "url", -] diff --git a/modules/orgs/Cargo.toml b/modules/orgs/Cargo.toml deleted file mode 100644 index 022fa5c6..00000000 --- a/modules/orgs/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "orgs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[profile.release] -panic = "abort" -opt-level = "s" -lto = true - -[dependencies] -anyhow = "1.0" -bincode = "1.3.3" -cargo-component-bindings = { git = "https://github.com/bytecodealliance/cargo-component" } -serde = {version = "1.0", features = ["derive"] } -serde_json = "1.0" -wit-bindgen = { version = "0.11.0", default_features = false } -base64 = "0.13" - -[lib] -crate-type = ["cdylib"] - -[package.metadata.component] -package = "component:uq-process" - -[package.metadata.component.target] -path = "wit" - -[package.metadata.component.dependencies] diff --git a/modules/orgs/pkg/manifest.json b/modules/orgs/pkg/manifest.json deleted file mode 100644 index 8648eb09..00000000 --- a/modules/orgs/pkg/manifest.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "process_name": "orgs", - "process_wasm_path": "/orgs.wasm", - "on_panic": "Restart", - "request_networking": true, - "request_messaging": [ - "http_bindings:http_bindings:uqbar" - ], - "public": false - } -] diff --git a/modules/orgs/pkg/metadata.json b/modules/orgs/pkg/metadata.json deleted file mode 100644 index 43e641c7..00000000 --- a/modules/orgs/pkg/metadata.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "package": "orgs", - "publisher": "uqbar", - "version": [0, 1, 0] -} diff --git a/modules/orgs/src/lib.rs b/modules/orgs/src/lib.rs deleted file mode 100644 index e33d9c35..00000000 --- a/modules/orgs/src/lib.rs +++ /dev/null @@ -1,1763 +0,0 @@ -cargo_component_bindings::generate!(); - -use bindings::component::uq_process::types::*; -use bindings::{ - get_payload, print_to_terminal, receive, save_capabilities, send_and_await_response, - send_request, send_requests, send_response, Guest, -}; -use serde::{Deserialize, Serialize}; -use serde_json::{json, to_vec}; -use std::collections::HashMap; -extern crate base64; - -#[allow(dead_code)] -mod process_lib; - -struct Component; - -type Contact = HashMap; - -#[derive(Clone, Debug, Serialize, Deserialize)] -struct OrgChat { - id: i64, - invite_link: String, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -struct Member { - username: String, - is_admin: bool, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -struct Org { - id: u64, - owner: String, - name: String, - description: String, - members: HashMap, - chats: HashMap, - created: u64, - updated: u64, -} - -type Orgs = HashMap; - -#[derive(Clone, Debug, Serialize, Deserialize)] -struct TelegramChat { - id: i64, - title: String, - chat_type: String, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -struct TelegramBot { - id: u64, - is_bot: bool, - first_name: String, - username: String, - can_join_groups: bool, - can_read_all_group_messages: bool, - supports_inline_queries: bool, - token: String, - chats: HashMap, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -struct OrgsState { - pub our_contact_info: Contact, - pub address_book: HashMap, - pub requester_updates: HashMap, - pub orgs: Orgs, - pub telegram_bots: HashMap, -} - -fn generate_http_binding( - add: Address, - path: &str, - authenticated: bool, -) -> (Address, Request, Option, Option) { - ( - add, - Request { - inherit: false, - expects_response: None, - ipc: json!({ - "BindPath": { - "path": path, - "authenticated": authenticated, - "local_only": false - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ) -} - -fn get_http_request_info( - message_json: serde_json::Value, -) -> ( - String, // method - String, // path - String, // raw_path - serde_json::Value, // headers - serde_json::Value, // url_params - serde_json::Value, // query_params -) { - let method = message_json["method"].as_str().unwrap_or("").to_string(); - let path = message_json["path"].as_str().unwrap_or("").to_string(); - let raw_path = message_json["raw_path"].as_str().unwrap_or("").to_string(); - let headers = message_json["headers"].clone(); - let url_params = message_json["url_params"].clone(); - let query_params = message_json["query_params"].clone(); - - (method, path, raw_path, headers, url_params, query_params) -} - -fn send_http_response(status: u16, headers: HashMap, payload_bytes: Vec) { - send_response( - &Response { - inherit: false, - ipc: json!({ - "status": status, - "headers": headers, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: payload_bytes, - }), - ) -} - -fn get_response_info( - response: Result<(Address, Message), SendError>, -) -> (Vec, Option, Option>) { - match response { - Ok((_source, message)) => { - if let Message::Response((response, context)) = message { - (response.ipc, get_payload(), context) - } else { - (vec![], None, None) - } - } - Err(_) => (vec![], None, None), - } -} - -fn send_http_client_request( - our_name: String, - url: String, - method: &str, - headers: HashMap, - body: Vec, - context: Option<&Vec>, -) { - send_request( - &Address { - node: our_name, - process: ProcessId::from_str("http_client:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), // TODO evaluate timeout - ipc: json!({ - "method": method, - "uri": url, - "headers": headers, - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - context, - Some(&Payload { - mime: Some("application/octet-stream".to_string()), - bytes: body, - }), - ) -} - -fn call_telegram_api( - our_name: String, - token: String, - path: String, - method: &str, - body: serde_json::Value, -) { - send_http_client_request( - our_name.clone(), - format!("https://api.telegram.org/bot{}/{}", token, path), - method, - { - let mut headers = HashMap::new(); - headers.insert("Content-Type".to_string(), "application/json".to_string()); - headers - }, - body.to_string().as_bytes().to_vec(), - None, - ) -} - -fn modify_telegram_membership( - org: &Org, - our_name: String, - telegram_bots: &HashMap, - address_book: &HashMap, - username: String, - action: &str, -) { - if let Some(chat) = org.chats.get("telegram") { - // find the right bot for this chat - for b in telegram_bots.values() { - if b.chats.contains_key(&chat.id) { - if let Some(contact) = address_book.get(&username) { - if let Some(telegram_id) = contact.get("telegram_id") { - // print_to_terminal(0, format!("orgs: {} USER {}", action.to_string(), telegram_id).as_str()); - if let Ok(telegram_id) = telegram_id.parse::() { - call_telegram_api( - our_name, - b.token.clone(), - action.to_string(), - "POST", - json!({ - "chat_id": chat.id, - "user_id": telegram_id, - }), - ); - } - } - } - break; - } - } - } -} - -fn handle_telegram_update( - our_name: String, - bot_id: u64, - json: serde_json::Value, - orgs: &mut Orgs, - telegram_bots: &mut HashMap, - address_book: &mut HashMap, -) -> Option { - let update_result = json["result"].clone(); - let mut update_id: Option = None; - let Some(bot_data) = telegram_bots.get_mut(&bot_id) else { - return update_id; - }; - - if let Some(result_array) = update_result.as_array() { - for result in result_array { - if let Some(result_object) = result.as_object() { - update_id = match result_object.get("update_id") { - Some(update_id) => match update_id.as_u64() { - Some(update_id) => Some(update_id), - None => None, - }, - None => None, - }; - - if let Some(message) = result_object.get("message") { - // handle everything here - let chat = &message["chat"]; - let Some(chat_id) = chat["id"].as_i64() else { - return None; - }; - let existing_chat = bot_data.chats.get(&chat_id); - - if existing_chat.is_none() { - let telegram_chat = TelegramChat { - id: chat_id, - title: chat["title"].as_str().unwrap_or_default().to_string(), - chat_type: chat["type"].as_str().unwrap_or_default().to_string(), - }; - bot_data.chats.insert(chat_id, telegram_chat.clone()); - - call_telegram_api( - our_name.clone(), - bot_data.token.clone(), - "sendMessage".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "text": format!("I have registered this chat in the API manager! ({})", chat["title"].as_str().unwrap()) - }), - ); - - send_request( - &Address { - node: our_name.clone(), - process: ProcessId::from_str("encryptor:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: None, - ipc: json!({ - "EncryptAndForwardAction": { - "channel_id": "orgs", - "forward_to": { - "node": our_name.clone(), - "process": { - "process_name": "http_server", - "package_name": "sys", - "publisher_node": "uqbar" - } - }, - "json": Some(json!({ // this is the JSON to forward - "WebSocketPush": { - "target": { - "node": our_name.clone(), - "id": "orgs", // If the message passed in an ID then we could send to just that ID - } - } - })), - } - - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: json!({ - "kind": "telegram_chat_added", - "data": { - "bot_id": bot_id, - "chat": telegram_chat, - } - }) - .to_string() - .as_bytes() - .to_vec(), - }), - ); - - let response = send_and_await_response( - &Address { - node: our_name.clone(), - process: ProcessId::from_str("http_client:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), // TODO evaluate timeout - ipc: json!({ - "method": "GET", - "uri": format!("https://api.telegram.org/bot{}/getChatAdministrators", bot_data.token), - "headers": { - "Content-Type": "application/json", - }, - }).to_string().as_bytes().to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: json!({ - "chat_id": chat_id, - }).to_string().as_bytes().to_vec(), - }), - ); - - match get_response_info(response) { - (ipc, Some(payload), _) => { - let json = - serde_json::from_slice::(&payload.bytes) - .unwrap(); - if let Some(admins) = json["result"].as_array() { - // Iterate over the admins and check if the bot is an admin - for admin in admins { - let user_id = - admin["user"]["id"].as_u64().unwrap_or_default(); - let can_manage_chat = - admin["can_manage_chat"].as_bool().unwrap_or_default(); - - if user_id == bot_id && !can_manage_chat { - call_telegram_api( - our_name.clone(), - bot_data.token.clone(), - "sendMessage".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "text": "Please go to chat info and make me an admin so that I can manage this chat. You should allow me to \"Invite Users via Link\"." - }), - ); - } - } - } - } - _ => (), - } - print_to_terminal(0, "1.5"); - } - - if let Some(chat_join_request) = message["chat_join_request"].as_object() { - let Some(chat_id) = chat["id"].as_i64() else { - return None; - }; - let from = &chat_join_request["from"]; - - // do a for loop over orgs and check if the chat_id is in any of the orgs - for org in orgs.values() { - if let Some(telegram_chat) = org.chats.get("telegram") { - if telegram_chat.id == chat_id { - // this is the org we want - let mut is_member = false; - for (member, _) in &org.members { - if let Some(mut contact) = address_book.get_mut(member) { - if contact - .get("telegram_username") - .unwrap_or(&"".to_string()) - == from["username"].as_str().unwrap_or("none") - { - contact.insert( - "telegram_id".to_string(), - from["id"].to_string(), - ); - call_telegram_api( - our_name.clone(), - bot_data.token.clone(), - "unbanChatMember".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "user_id": from["id"], - }), - ); - call_telegram_api( - our_name.clone(), - bot_data.token.clone(), - "approveChatJoinRequest".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "user_id": from["id"], - }), - ); - - is_member = true; - break; - } - } - } - - if !is_member { - print_to_terminal(0, "7"); - call_telegram_api( - our_name.clone(), - bot_data.token.clone(), - "declineChatJoinRequest".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "user_id": from["id"], - }), - ); - } - } - } - } - } else if let Some(new_chat_members) = message["new_chat_members"].as_array() { - let Some(chat_id) = chat["id"].as_i64() else { - return None; - }; - for ncm in new_chat_members.iter() { - for org in orgs.values() { - if let Some(telegram_chat) = org.chats.get("telegram") { - if telegram_chat.id == chat_id { - for (member, _) in &org.members { - if let Some(mut contact) = address_book.get_mut(member) - { - if let Some(telegram_username) = - contact.get("telegram_username") - { - if ncm["username"].as_str().unwrap_or("none") - == telegram_username - { - contact.insert( - "telegram_id".to_string(), - ncm["id"].to_string(), - ); - return update_id; - } - } - } - } - } - } - } - - call_telegram_api( - our_name.clone(), - bot_data.token.clone(), - "banChatMember".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "user_id": ncm["id"], - }), - ); - call_telegram_api( - our_name.clone(), - bot_data.token.clone(), - "unbanChatMember".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "user_id": ncm["id"], - }), - ); - } - } - } - } - } - } - - update_id -} - -fn self_is_admin(orgs: &Orgs, our_name: String, org_id: u64) -> bool { - if let Some(org) = orgs.get(&org_id) { - if let Some(member) = org.members.get(&our_name) { - return member.is_admin; - } - } - - false -} - -fn sum_char_codes(s: &str) -> u64 { - s.chars().map(|c| c as u64).sum() -} - -fn serve_html(our: Address, default_headers: HashMap) { - let response = send_and_await_response( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("vfs:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), // TODO evaluate timeout - ipc: json!({ - "GetEntry": { - "drive": "orgs_static", - "full_path": "/index.html" - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - ); - - if let Some(payload) = get_payload() { - send_http_response(200, default_headers.clone(), payload.bytes); - } else { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - } -} - -fn serve_static(raw_path: &str, our: Address, default_headers: HashMap) { - if let Some(file_path) = raw_path.strip_prefix("/orgs/static") { - let mut headers = HashMap::new(); - let content_type = match file_path.split(".").last() { - Some("css") => "text/css", - Some("js") => "application/javascript", - Some("png") => "image/png", - Some("jpg") => "image/jpeg", - Some("jpeg") => "image/jpeg", - Some("gif") => "image/gif", - Some("svg") => "image/svg+xml", - _ => "text/plain", - }; - headers.insert("Content-Type".to_string(), content_type.to_string()); - - let response = send_and_await_response( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("vfs:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), // TODO evaluate timeout - ipc: json!({ - "GetEntry": { - "drive": "orgs_static", - "full_path": file_path // everything after "/orgs/static" - } - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - ); - - if let Some(payload) = get_payload() { - send_http_response(200, headers, payload.bytes); - } else { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - } - } else { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - } -} - -impl Guest for Component { - fn init(our: Address) { - print_to_terminal(0, "orgs: start"); - - let mut state: OrgsState = match bindings::get_state() { - Some(bytes) => match serde_json::from_slice::(&bytes) { - Ok(state) => state, - Err(_) => OrgsState { - our_contact_info: HashMap::new(), - address_book: HashMap::new(), - requester_updates: HashMap::new(), - orgs: HashMap::new(), - telegram_bots: HashMap::new(), - }, - }, - None => OrgsState { - our_contact_info: HashMap::new(), - address_book: HashMap::new(), - requester_updates: HashMap::new(), - orgs: HashMap::new(), - telegram_bots: HashMap::new(), - }, - }; - - // call set_state(our.node.clone(), bytes) whenever anything changes - - let bindings_address = Address { - node: our.node.clone(), - process: ProcessId::from_str("http_server:sys:uqbar").unwrap(), - }; - - // , option> - let http_endpoint_binding_requests: [(Address, Request, Option, Option); - 7] = [ - generate_http_binding(bindings_address.clone(), "/", false), - generate_http_binding(bindings_address.clone(), "static/*", false), - generate_http_binding(bindings_address.clone(), "/my-info", false), - generate_http_binding(bindings_address.clone(), "/list", false), - generate_http_binding(bindings_address.clone(), "/:org_id/members", false), - generate_http_binding(bindings_address.clone(), "/:org_id/chats", false), - generate_http_binding(bindings_address.clone(), "/:platform/bots", false), - ]; - send_requests(&http_endpoint_binding_requests); - - loop { - let Ok((source, message)) = receive() else { - print_to_terminal(0, "orgs: got network error"); - // TODO: handle network error. These will almost always be orgs updates or address_book updates - continue; - }; - // TODO: handle the Message::Response case. This will be for telegram bot messages sent to http_client - match message { - Message::Request(request) => { - let message_json: serde_json::Value = match serde_json::from_slice(&request.ipc) - { - Ok(v) => v, - Err(_) => { - print_to_terminal(0, "orgs: failed to parse ipc JSON, skipping"); - continue; - } - }; - - if message_json["action"] == "transfer_capability" { - print_to_terminal(1, "orgs: transfer_capability"); - if let Some(payload) = get_payload() { - let signature = payload.bytes; - let node = message_json["info"]["issuer"]["node"] - .as_str() - .unwrap_or("") - .to_string(); - let process = message_json["info"]["issuer"]["process"] - .as_str() - .unwrap_or("") - .to_string(); - let params = message_json["info"]["params"] - .as_str() - .unwrap_or("") - .to_string(); - - if node == "" || process == "" || params == "" { - print_to_terminal( - 1, - "orgs: transfer_capability: missing node, process, or params", - ); - continue; - } - - save_capabilities(&[SignedCapability { - issuer: Address { - node, - process: ProcessId::from_str(&process).unwrap(), - }, - params, - signature, - }]); - } - } else if message_json["action"] == "get_contact_info" { - print_to_terminal(1, "orgs: get_contact_info"); - send_response( - &Response { - inherit: false, - ipc: json!({ - "action": "get_contact_info", - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: json!(&state.our_contact_info) - .to_string() - .as_bytes() - .to_vec(), - }), - ); - continue; - } else if message_json["action"] == "update_contact_info" { - if let Some(payload) = get_payload() { - if let Ok(contact_info) = - serde_json::from_slice::(&payload.bytes) - { - state - .address_book - .insert(source.node.clone(), contact_info.clone()); - bindings::set_state(&to_vec(&state).unwrap()); - send_response( - &Response { - inherit: false, - ipc: json!({ - "action": "update_contact_info", - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - ); - }; - } - continue; - } else if message_json["action"] == "update_orgs" { - if let Some(payload) = get_payload() { - if let Ok(org) = serde_json::from_slice::(&payload.bytes) { - state.orgs.insert(org.id, org); - send_response( - &Response { - inherit: false, - ipc: json!({ - "action": "update_orgs", - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - ); - }; - } - continue; - } else if source.node == our.node - && source.process.to_string() == "http_server:sys:uqbar" - { - // Handle http request - let mut default_headers = HashMap::new(); - default_headers.insert("Content-Type".to_string(), "text/html".to_string()); - - let (method, path, raw_path, headers, url_params, query_params) = - get_http_request_info(message_json.clone()); - - match method.as_str() { - "GET" => match path.as_str() { - "/" => serve_html(our.clone(), default_headers.clone()), - "static/*" => { - serve_static(&raw_path, our.clone(), default_headers.clone()) - } - "/my-info" => { - send_http_response( - 200, - default_headers.clone(), - json!(&state.our_contact_info) - .to_string() - .as_bytes() - .to_vec(), - ); - } - "/list" => { - send_http_response( - 200, - { - let mut headers = HashMap::new(); - headers.insert( - "Content-Type".to_string(), - "application/json".to_string(), - ); - headers - }, - json!(&state.orgs).to_string().as_bytes().to_vec(), - ); - } - "/:platform/bots" => { - if url_params["platform"] == "telegram" { - send_http_response( - 200, - { - let mut headers = HashMap::new(); - headers.insert( - "Content-Type".to_string(), - "application/json".to_string(), - ); - headers - }, - json!(&state.telegram_bots) - .to_string() - .as_bytes() - .to_vec(), - ); - } else { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - } - } - _ => send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ), - }, - "POST" => { - print_to_terminal(0, format!("POST: {}", path).as_str()); - let Some(payload) = get_payload() else { - print_to_terminal( - 0, - "orgs: no bytes in payload, skipping...", - ); - send_http_response( - 400, - default_headers.clone(), - "No payload".to_string().as_bytes().to_vec(), - ); - continue; - }; - - match path.as_str() { - "/" => { - let Ok(org) = serde_json::from_slice::( - &payload.bytes, - ) else { - print_to_terminal(0, "orgs: JSON is not valid"); - send_http_response( - 400, - default_headers.clone(), - "Invalid JSON".to_string().as_bytes().to_vec(), - ); - continue; - }; - - if let Some(name) = org["name"].as_str() { - let org_id = sum_char_codes(name); - - let mut org = Org { - id: sum_char_codes(name), - owner: our.node.clone(), - name: name.to_string(), - description: org["description"] - .as_str() - .unwrap_or("") - .to_string(), - members: HashMap::new(), - chats: HashMap::new(), - created: 0, - updated: 0, - }; - org.members.insert( - our.node.clone(), - Member { - username: our.node.clone(), - is_admin: true, - }, - ); - - state.orgs.insert(org.id.clone(), org.clone()); - send_http_response( - 201, - default_headers.clone(), - json!(org).to_string().as_bytes().to_vec(), - ); - } else { - send_http_response( - 400, - default_headers.clone(), - "Invalid Org Name".to_string().as_bytes().to_vec(), - ); - } - } - "/my-info" => { - let Ok(my_info) = - serde_json::from_slice::>( - &payload.bytes, - ) - else { - print_to_terminal(0, "orgs: JSON is not valid"); - send_http_response( - 400, - default_headers.clone(), - "Invalid JSON".to_string().as_bytes().to_vec(), - ); - continue; - }; - - for (key, value) in my_info { - state.our_contact_info.insert(key, value); - } - - bindings::set_state(&to_vec(&state).unwrap()); - - send_http_response( - 201, - default_headers.clone(), - "Created".to_string().as_bytes().to_vec(), - ); - } - "/:org_id/members" => { - if !self_is_admin( - &state.orgs, - our.node.clone(), - url_params["org_id"] - .as_str() - .unwrap_or("") - .parse::() - .unwrap_or(0), - ) { - send_http_response( - 403, - default_headers.clone(), - "Forbidden".to_string().as_bytes().to_vec(), - ); - continue; - } - let Ok(json) = - serde_json::from_slice::( - &payload.bytes, - ) - else { - print_to_terminal(0, "orgs: Username is not valid"); - send_http_response( - 400, - default_headers.clone(), - "Invalid Username" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - }; - - let Some(username_str) = json["member"].as_str() else { - print_to_terminal(0, "orgs: Username is not valid"); - send_http_response( - 400, - default_headers.clone(), - "Invalid Username" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - }; - let username = username_str.to_string(); - let is_admin = json["is_admin"].as_bool().unwrap_or(false); - - let org_id = match url_params["org_id"] - .as_str() - .unwrap_or("0") - .parse::() - { - Ok(value) => value, - Err(e) => { - print_to_terminal( - 1, - format!("orgs: failed to parse org_id: {}", e) - .as_str(), - ); - send_http_response( - 400, - default_headers.clone(), - "Invalid Org ID" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - } - }; - - if let Some(org) = state.orgs.get_mut(&org_id) { - org.members.insert( - username.clone(), - Member { - username: username.clone(), - is_admin, - }, - ); - // Get contact info for the user - send_request( - &Address { - node: username.clone(), - process: ProcessId::from_str("orgs:sys:uqbar") - .unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), // TODO evaluate timeout - ipc: json!({ - "action": "get_contact_info", - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - None, - ); - // Send the org to the user - send_request( - &Address { - node: username.clone(), - process: ProcessId::from_str("orgs:sys:uqbar") - .unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(15), // TODO evaluate timeout - ipc: json!({ - "action": "update_orgs", - }) - .to_string() - .as_bytes() - .to_vec(), - metadata: None, - }, - None, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: json!(&org) - .to_string() - .as_bytes() - .to_vec(), - }), - ); - send_http_response( - 201, - default_headers.clone(), - "Created".to_string().as_bytes().to_vec(), - ); - } else { - send_http_response( - 400, - default_headers.clone(), - "Invalid Org ID".to_string().as_bytes().to_vec(), - ); - } - } - "/:org_id/chats" => { - if !self_is_admin( - &state.orgs, - our.node.clone(), - url_params["org_id"] - .as_str() - .unwrap_or("") - .parse::() - .unwrap_or(0), - ) { - send_http_response( - 403, - default_headers.clone(), - "Forbidden".to_string().as_bytes().to_vec(), - ); - continue; - } - let Ok(body) = - serde_json::from_slice::( - &payload.bytes, - ) - else { - print_to_terminal(0, "orgs: JSON is not valid"); - send_http_response( - 400, - default_headers.clone(), - "Invalid JSON".to_string().as_bytes().to_vec(), - ); - continue; - }; - let org_id = match url_params["org_id"] - .as_str() - .unwrap_or("0") - .parse::() - { - Ok(value) => value, - Err(e) => { - print_to_terminal( - 1, - format!("orgs: failed to parse org_id: {}", e) - .as_str(), - ); - send_http_response( - 400, - default_headers.clone(), - "Invalid Org ID" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - } - }; - - let chat_id = body["id"].as_i64().unwrap_or_default(); - let platform = - body["platform"].as_str().unwrap_or_default(); - - let mut bot: Option = None; - for b in state.telegram_bots.values() { - if b.chats.contains_key(&chat_id) { - bot = Some(b.clone()); - break; - } - } - - if let Some(bot) = bot { - let response = send_and_await_response( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("http_client:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), // TODO evaluate timeout - ipc: json!({ - "method": "GET", - "uri": format!("https://api.telegram.org/bot{}/getChat", bot.token.clone()), - "headers": { - "Content-Type": "application/json", - }, - }).to_string().as_bytes().to_vec(), - metadata: None, - }, - Some(&Payload { - mime: Some("application/json".to_string()), - bytes: json!({ - "chat_id": chat_id, - }).to_string().as_bytes().to_vec(), - }), - ); - print_to_terminal(0, "2"); - - let Some(response_payload) = get_payload() else { - print_to_terminal( - 0, - "orgs: no payload in response", - ); - send_http_response( - 500, - default_headers.clone(), - "Unable to get chat invite link" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - }; - - let json = serde_json::from_slice::( - &response_payload.bytes, - ); - print_to_terminal(0, "3"); - - if let Ok(result_json) = json { - let invite_link = result_json["result"] - ["invite_link"] - .as_str() - .unwrap_or_default() - .to_string(); - // print invite_link - print_to_terminal( - 1, - format!("orgs: invite link {}", invite_link) - .as_str(), - ); - // print org_id - print_to_terminal( - 1, - format!("orgs: org_id {}", org_id).as_str(), - ); - if let Some(org) = state.orgs.get_mut(&org_id) { - org.chats.insert( - platform.to_string(), - OrgChat { - id: chat_id, - invite_link: invite_link, - }, - ); - print_to_terminal(0, "4"); - - for (member, _) in &org.members { - if let Some(contact) = - state.address_book.get(member) - { - if let Some(telegram_username) = - contact.get("telegram_username") - { - call_telegram_api( - our.node.clone(), - bot.token.clone(), - "unbanChatMember".to_string(), - "POST", - json!({ - "chat_id": chat_id, - "user_id": telegram_username, - }), - ); - } - } - } - print_to_terminal(0, "5"); - - send_http_response( - 201, - default_headers.clone(), - json!(org).to_string().as_bytes().to_vec(), - ); - } else { - send_http_response( - 500, - default_headers.clone(), - "Unable to get chat invite link" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } else { - send_http_response( - 500, - default_headers.clone(), - "Unable to get chat invite link" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } else { - send_http_response( - 400, - default_headers.clone(), - "Invalid Chat ID".to_string().as_bytes().to_vec(), - ); - } - } - "/:platform/bots" => { - if message_json["url_params"]["platform"] == "telegram" { - let Ok(token) = String::from_utf8(payload.bytes) - else { - print_to_terminal(0, "orgs: no token for bot"); - send_http_response( - 400, - default_headers.clone(), - "Invalid JSON" - .to_string() - .as_bytes() - .to_vec(), - ); - continue; - }; - - // Check if the bot already exists - let response = send_and_await_response( - &Address { - node: our.node.clone(), - process: ProcessId::from_str("http_client:sys:uqbar").unwrap(), - }, - &Request { - inherit: false, - expects_response: Some(5), // TODO evaluate timeout - ipc: json!({ - "method": "GET", - "uri": format!("https://api.telegram.org/bot{}/getMe", token), - "headers": { - "Content-Type": "application/json", - }, - }).to_string().as_bytes().to_vec(), - metadata: None, - }, - None, - ); - - let bot: Option = match get_response_info( - response, - ) { - (ipc, Some(payload), _) => { - let json = serde_json::from_slice::< - serde_json::Value, - >( - &ipc - ) - .unwrap(); - if json["status"].as_u64().unwrap_or_default() - < 300 - { - match serde_json::from_slice::< - serde_json::Value, - >( - &payload.bytes - ) { - Ok(bot_json) => { - Some(TelegramBot { - id: bot_json["result"]["id"].as_u64().unwrap_or(0), - is_bot: bot_json["result"]["is_bot"].as_bool().unwrap_or(false), - first_name: bot_json["result"]["first_name"].as_str().unwrap_or("").to_string(), - username: bot_json["result"]["username"].as_str().unwrap_or("").to_string(), - can_join_groups: bot_json["result"]["can_join_groups"].as_bool().unwrap_or(false), - can_read_all_group_messages: bot_json["result"]["can_read_all_group_messages"].as_bool().unwrap_or(false), - supports_inline_queries: bot_json["result"]["supports_inline_queries"].as_bool().unwrap_or(false), - token: token, - chats: HashMap::new(), - }) - } - Err(_) => None, - } - } else { - None - } - } - _ => None, - }; - - if let Some(bot) = bot { - let bot_id = bot.id.clone(); - let bot_token = bot.token.clone(); - state - .telegram_bots - .insert(bot_id.clone(), bot.clone()); - send_http_client_request( - our.node.clone(), - format!( - "https://api.telegram.org/bot{}/getUpdates", - bot_token - ), - "GET", - HashMap::new(), - Vec::new(), - Some(&json!({ - "telegram_bot_id": bot_id - }) - .to_string() - .as_bytes() - .to_vec()), - ); - send_http_response( - 201, - default_headers.clone(), - serde_json::to_string(&bot) - .unwrap_or_default() - .as_bytes() - .to_vec(), - ); - } else { - send_http_response( - 500, - default_headers.clone(), - "Unable to create bot" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } else { - send_http_response( - 400, - default_headers.clone(), - "Invalid Bot Platform" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } - _ => send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ), - } - } - "PUT" => { - let Some(payload) = get_payload() else { - print_to_terminal( - 0, - "orgs: no bytes in payload, skipping...", - ); - send_http_response( - 400, - default_headers.clone(), - "No payload".to_string().as_bytes().to_vec(), - ); - continue; - }; - let body_json = match serde_json::from_slice(&payload.bytes) { - Ok(v) => v, - Err(_) => { - print_to_terminal(0, "orgs: JSON is not valid"); - send_http_response( - 400, - default_headers.clone(), - "Invalid JSON".to_string().as_bytes().to_vec(), - ); - continue; - } - }; - - match path.as_str() { - "/" => {} - _ => send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ), - } - } - "DELETE" => match path.as_str() { - "/" => {} - "/:org_id/members" => { - let username = query_params["username"].as_str().unwrap_or(""); - let org_id = match url_params["org_id"] - .as_str() - .unwrap_or("0") - .parse::() - { - Ok(value) => value, - Err(e) => { - print_to_terminal( - 1, - format!("orgs: failed to parse org_id: {}", e) - .as_str(), - ); - send_http_response( - 400, - default_headers.clone(), - "Invalid Org ID".to_string().as_bytes().to_vec(), - ); - continue; - } - }; - if let Some(org) = state.orgs.get_mut(&org_id) { - modify_telegram_membership( - org, - our.node.clone(), - &state.telegram_bots, - &state.address_book, - username.to_string(), - "banChatMember", - ); - modify_telegram_membership( - org, - our.node.clone(), - &state.telegram_bots, - &state.address_book, - username.to_string(), - "unbanChatMember", - ); - org.members.remove(username); - send_http_response( - 200, - default_headers.clone(), - "OK".to_string().as_bytes().to_vec(), - ); - } else { - send_http_response( - 400, - default_headers.clone(), - "Invalid Org ID".to_string().as_bytes().to_vec(), - ); - } - } - "/:org_id/chats" => { - let platform = query_params["platform"].as_str().unwrap_or(""); - let org_id = match url_params["org_id"] - .as_str() - .unwrap_or("0") - .parse::() - { - Ok(value) => value, - Err(e) => { - print_to_terminal( - 1, - format!("orgs: failed to parse org_id: {}", e) - .as_str(), - ); - send_http_response( - 400, - default_headers.clone(), - "Invalid Org ID".to_string().as_bytes().to_vec(), - ); - continue; - } - }; - if let Some(org) = state.orgs.get_mut(&org_id) { - if let Some(chat) = org.chats.get(platform) { - org.chats.remove(platform); - send_http_response( - 200, - default_headers.clone(), - "OK".to_string().as_bytes().to_vec(), - ); - } else { - send_http_response( - 400, - default_headers.clone(), - "Invalid Chat Platform" - .to_string() - .as_bytes() - .to_vec(), - ); - } - } else { - send_http_response( - 400, - default_headers.clone(), - "Invalid Org ID".to_string().as_bytes().to_vec(), - ); - } - } - "/:platform/bots" => { - let platform = url_params["platform"].as_str().unwrap_or(""); - let bot_id = match url_params["id"] - .as_str() - .unwrap_or("") - .parse::() - { - Ok(value) => value, - Err(e) => { - print_to_terminal( - 1, - format!("orgs: failed to parse bot_id: {}", e) - .as_str(), - ); - send_http_response( - 400, - default_headers.clone(), - "Invalid Bot ID".to_string().as_bytes().to_vec(), - ); - continue; - } - }; - // 1. Delete all chats in all orgs managed by this bot - for org in state.orgs.values_mut() { - let mut has_chat = false; - if let Some(chat) = org.chats.get_mut(platform) { - for bot in state.telegram_bots.values() { - if bot.chats.contains_key(&chat.id) { - has_chat = true; - break; - } - } - } - if has_chat { - org.chats.remove(platform); - } - } - // 2. Delete the bot from bots - state.telegram_bots.remove(&bot_id); - } - _ => { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - continue; - } - }, - _ => { - send_http_response( - 404, - default_headers.clone(), - "Not Found".to_string().as_bytes().to_vec(), - ); - continue; - } - } - } else { - // Handling WS messages here - if let Some(payload) = get_payload() { - // TODO: make a message system here - if let Ok(json) = - serde_json::from_slice::(&payload.bytes) - { - print_to_terminal(0, format!("JSON: {}", json).as_str()); - // Handle the websocket messages - } - } - } - } - Message::Response((response, context)) => { - if source.process.to_string() == "http_client:sys:uqbar" { - let Some(context) = context else { - print_to_terminal(0, "orgs: got response without context"); - continue; - }; - let Ok(context) = serde_json::from_slice::(&context) - else { - print_to_terminal(0, "orgs: context is not valid JSON"); - continue; - }; - - let telegram_bot_id = context["telegram_bot_id"].as_u64().unwrap_or(0); - - if telegram_bot_id != 0 { - let Some(payload) = get_payload() else { - print_to_terminal( - 0, - "orgs: no bytes in response payload, skipping...", - ); - continue; - }; - - let json = - match serde_json::from_slice::(&payload.bytes) { - Ok(v) => v, - Err(_) => { - print_to_terminal(0, "orgs: JSON is not valid"); - continue; - } - }; - - print_to_terminal(1, format!("orgs: response JSON {}", json).as_str()); - - let update_id = handle_telegram_update( - our.node.clone(), - telegram_bot_id, - json, - &mut state.orgs, - &mut state.telegram_bots, - &mut state.address_book, - ); - let token = state - .telegram_bots - .get(&telegram_bot_id) - .unwrap() - .token - .clone(); - - let uri = match update_id { - Some(id) => format!( - "https://api.telegram.org/bot{}/getUpdates?offset={}", - token, - id + 1 - ), - None => format!("https://api.telegram.org/bot{}/getUpdates", token), - }; - - if state.telegram_bots.contains_key(&telegram_bot_id) { - send_http_client_request( - our.node.clone(), - uri, - "GET", - HashMap::new(), - Vec::new(), - Some( - &serde_json::json!({ - "telegram_bot_id": telegram_bot_id - }) - .to_string() - .as_bytes() - .to_vec(), - ), - ); - } - } - } else if source.process.to_string() == "orgs:sys:uqbar" { - let message_json: serde_json::Value = - match serde_json::from_slice(&response.ipc) { - Ok(v) => v, - Err(_) => { - print_to_terminal(0, "orgs: failed to parse ipc JSON"); - continue; - } - }; - - if message_json["action"] == "get_contact_info" { - let Some(payload) = get_payload() else { - print_to_terminal( - 0, - "orgs: no bytes in response payload, skipping...", - ); - continue; - }; - - let contact_info = - match serde_json::from_slice::(&payload.bytes) { - Ok(v) => v, - Err(_) => { - print_to_terminal(0, "orgs: failed to parse contact"); - continue; - } - }; - - state - .address_book - .insert(source.node.clone(), contact_info.clone()); - } - } else { - print_to_terminal(0, "orgs: got unexpected response"); - continue; - } - } - } - } - } -} diff --git a/modules/orgs/src/process_lib.rs b/modules/orgs/src/process_lib.rs deleted file mode 120000 index 77367fe0..00000000 --- a/modules/orgs/src/process_lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../src/process_lib.rs \ No newline at end of file From dcc61e8ad4b07a887614cfe51948cb293d2db094 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 22:09:59 -0500 Subject: [PATCH 39/40] Merge branch 'main' into dr/process-std-lib --- modules/qns_indexer/Cargo.lock | 29 +++++++++++++++++++++++++++++ modules/qns_indexer/Cargo.toml | 1 + 2 files changed, 30 insertions(+) diff --git a/modules/qns_indexer/Cargo.lock b/modules/qns_indexer/Cargo.lock index 61c5340a..0737a48a 100644 --- a/modules/qns_indexer/Cargo.lock +++ b/modules/qns_indexer/Cargo.lock @@ -113,6 +113,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.5.0" @@ -363,6 +369,7 @@ dependencies = [ "anyhow", "bincode", "hex", + "rmp-serde", "serde", "serde_json", "uqbar_process_lib", @@ -438,6 +445,28 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "ruint" version = "1.11.0" diff --git a/modules/qns_indexer/Cargo.toml b/modules/qns_indexer/Cargo.toml index aa3d610c..fc715523 100644 --- a/modules/qns_indexer/Cargo.toml +++ b/modules/qns_indexer/Cargo.toml @@ -16,6 +16,7 @@ alloy-primitives = "0.3.3" alloy-sol-types = "0.3.2" bincode = "1.3.3" hex = "0.4.3" +rmp-serde = "1.1.2" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" } From 2ae22c1e2626dbc12796c71be5f5cad1636984bf Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Mon, 6 Nov 2023 23:03:00 -0500 Subject: [PATCH 40/40] last bit of cleanup, everything critical working --- build.rs | 15 --------------- modules/app_store/app_store/src/lib.rs | 20 ++++++-------------- modules/http_proxy/src/lib.rs | 2 ++ modules/qns_indexer/src/lib.rs | 2 +- modules/terminal/src/lib.rs | 2 +- 5 files changed, 10 insertions(+), 31 deletions(-) diff --git a/build.rs b/build.rs index 6f0fb28b..2e1c40b5 100644 --- a/build.rs +++ b/build.rs @@ -153,21 +153,6 @@ fn main() { let entry_path = entry.unwrap().path(); let package_name = entry_path.file_name().unwrap().to_str().unwrap(); - if ![ - "app_store", - "chess", - "homepage", - "http_proxy", - "key_value", - "qns_indexer", - "sqlite", - "terminal", - ] - .contains(&package_name) - { - continue; - } - // If Cargo.toml is present, build the app let parent_pkg_path = format!("{}/pkg", entry_path.display()); if entry_path.join("Cargo.toml").exists() { diff --git a/modules/app_store/app_store/src/lib.rs b/modules/app_store/app_store/src/lib.rs index c75ea539..563b635b 100644 --- a/modules/app_store/app_store/src/lib.rs +++ b/modules/app_store/app_store/src/lib.rs @@ -4,8 +4,8 @@ use std::collections::{HashMap, HashSet}; use uqbar_process_lib::kernel_types as kt; use uqbar_process_lib::uqbar::process::standard as wit; use uqbar_process_lib::{ - get_capability, get_payload, get_typed_state, grant_messaging, receive, set_state, Address, - Message, NodeId, PackageId, ProcessId, Request, Response, + get_capability, get_payload, get_typed_state, grant_messaging, println, receive, set_state, + Address, Message, NodeId, PackageId, ProcessId, Request, Response, }; wit_bindgen::generate!({ @@ -158,15 +158,9 @@ pub enum InstallResponse { Failure, } -// -// app store init() -// - impl Guest for Component { fn init(our: String) { let our = Address::from_str(&our).unwrap(); - assert_eq!(our.process, "main:app_store:uqbar"); - // begin by granting messaging capabilities to http_server and terminal, // so that they can send us requests. grant_messaging( @@ -176,16 +170,14 @@ impl Guest for Component { ProcessId::from_str("terminal:terminal:uqbar").unwrap(), ]), ); - println!("app_store main proc: start"); + println!("{}: start", our.process); // load in our saved state or initalize a new one if none exists - let mut state = get_typed_state(|bytes| { - Ok(bincode::deserialize(bytes).unwrap_or(State { + let mut state = + get_typed_state(|bytes| Ok(bincode::deserialize(bytes)?)).unwrap_or(State { packages: HashMap::new(), requested_packages: HashSet::new(), - })) - }) - .unwrap(); + }); // active the main messaging loop: handle requests and responses loop { diff --git a/modules/http_proxy/src/lib.rs b/modules/http_proxy/src/lib.rs index bba2e600..2af32a7f 100644 --- a/modules/http_proxy/src/lib.rs +++ b/modules/http_proxy/src/lib.rs @@ -68,6 +68,7 @@ fn main(our: Address) -> anyhow::Result<()> { // bind to all of our favorite paths for path in ["/", "/static/*", "/list", "/register", "/serve/:username/*"] { Request::new() + .target(Address::new(&our.node, "http_server:sys:uqbar")?)? .ipc( &json!({ "BindPath": { @@ -273,6 +274,7 @@ fn main(our: Address) -> anyhow::Result<()> { } Request::new() + .target(Address::new(&username, "http_server:sys:uqbar")?)? .inherit(true) .ipc( &json!({ diff --git a/modules/qns_indexer/src/lib.rs b/modules/qns_indexer/src/lib.rs index 3e2ef1ca..bf78d058 100644 --- a/modules/qns_indexer/src/lib.rs +++ b/modules/qns_indexer/src/lib.rs @@ -104,7 +104,7 @@ fn subscribe_to_qns(from_block: u64) -> Vec { } fn serialize_message(message: &NetActions) -> anyhow::Result> { - Ok(serde_json::to_vec(message)?) + Ok(rmp_serde::to_vec(message)?) } fn serialize_json_message(message: &serde_json::Value) -> anyhow::Result> { diff --git a/modules/terminal/src/lib.rs b/modules/terminal/src/lib.rs index 255d4d8c..f3202da7 100644 --- a/modules/terminal/src/lib.rs +++ b/modules/terminal/src/lib.rs @@ -11,7 +11,7 @@ wit_bindgen::generate!({ }); fn serialize_message(message: &&str) -> anyhow::Result> { - Ok(serde_json::to_vec(message)?) + Ok(message.as_bytes().to_vec()) } fn parse_command(our_name: &str, line: &str) -> anyhow::Result<()> {